new addon based on the XBMC addon

This commit is contained in:
faush01 2014-09-28 10:30:07 +10:00
commit d348f04159
32 changed files with 10894 additions and 0 deletions

283
LICENSE.txt Normal file
View File

@ -0,0 +1,283 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Library General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
-------------------------------------------------------------------------
-------------------------------------------------------------------------

27
addon.xml Normal file
View File

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<addon id="plugin.video.xbmb3c"
name="XBMB3C"
version="0.9.546"
provider-name="xnappo, null_pointer, im85288">
<requires>
<import addon="script.module.requests" version="1.1.0" />
<import addon="xbmc.python" version="2.1.0"/>
<import addon="script.module.simplejson" version="2.0.10"/>
</requires>
<extension point="xbmc.python.pluginsource"
library="default.py">
<provides>executable video audio image</provides>
</extension>
<extension point="xbmc.service" library="service.py" start="startup">
</extension>
<extension point="xbmc.addon.metadata">
<platform>all</platform>
<language>en</language>
<license>GNU GENERAL PUBLIC LICENSE. Version 2, June 1991</license>
<forum>http://mediabrowser.tv/community/index.php?/forum/99-xbmb3c/</forum>
<website>http://mediabrowser.tv/</website>
<source>https://github.com/MediaBrowser/MediaBrowser.XBMC/</source>
<summary lang="en">Browse and play local video, music and photo media file managed by the Media Browser Server</summary>
<description lang="en">Browse and play local video, music and photo media file managed by the Media Browser Server</description>
</extension>
</addon>

163
changelog.txt Normal file
View File

@ -0,0 +1,163 @@
0.9.6
Features:
- Added MB3 Channels
- Added custom Item Info dialog
- Added a lot of new variables for use by skinners
- Changed behaviour of 'auto enter single season' - faster performance
- Added cache for 'All Movies' node.
- Added new 'search' dialog
- Added option to use 'poster' indicators for watched/in progress etc.
- Added option to sort 'Next Up' by show name.
- Option to disable CoverArt
- Various performance enhancements
- Added transcoding functionality
- Dutch language support
- Added unwatches movies/episodes nodes
- Added Genres, Studio, Actor nodes
- Added SuggestedItems nodes
- Partial implementation of new MB3 security
- Added flatten seasons option
- Support MusicVideos/HomeVideos in Recently Added
- Backdrops for user defines collections
- Support for 'media stubs' (physical media etc).
Bug fixes:
- Fix long XBMC close times
- Fixed incorrectly setting 'watched' under certain conditions
- Fixed rotating art for episodes
- Fixed percentage complete calculation
- More ArtWork fixes
- Multiple 'NoneType' fixes
- Don't offer 'Play from here' on folders
- Fix 'ItemInfo' 'Play' button (requires skin mod)
- Consider 'SortName' for collections
0.9.5
- Auto server discovery
- Cast Images
- Actor cross-referencing
- Additional artwork and metadata during playback
- Rotating backdrops for individual items
- Default views settings (must be supported by the skin)
- TV Theme music support
- Option to disable background services
- Proper artwork support at the Series/Season/Episode levels
- New method for skin widget support
- Proper sorting for Series and BoxSets
- Present episode counts and watch/unwatched counts
- Removed skin_diffs folder - use XBMB3C skin repo instead
0.9.0
- Search capability
- Better skin integration
- Added Favorite Shows node
- Update NextUp widget on stop
- Check that service is running
- Error handling for non-UNC paths
- Photo widget support
- Better MB3->XBMC art mapping
- Meta-data for playback in-progress items
- More granular debug logs
- No meta-data cache for less than 25 items (helps with TV status updates)
0.8.5
- Added remote control from other clients (null_pointer)
- Added trailer support (im85288)
- Added Couch Potato trailer integration (im85288)
- Updated to support server security update (xnappo)
- Server path substitution support (im85288)
- Added premier date and airtime to Upcoming TV (im85288)
- Added in-progress Movie and Episode entry points (im85288)
- Added percent text to in-progress items (null_pointer)
- Aeon Nox widget mods (Recently Added Moves/Episodes, NextUp Episodes) (xnappo)
- Added offer delete on episode played option (xnappo)
- Added optional progress dialog for large collections (null_pointer)
- Various improvements to data presentation (all)
0.8.0 - Improved cache accuracy (null_pointer)
- Added Confluence auto-menu creation
(add movies, then TV, then others to favorites, relaunch) (null_pointer)
- Added hooks for xperience1080++ automation. Gotham only! (im85288)
- Added much more art (disc art, clear art banner art etc). Gotham only! (im85288)
- Added total play time for boxsets (null_pointer)
- Provide skins boxset information (im85288)
- Added rotating background fanart (null_pointer/im85288)
- Use GZIP for JSON requests (null_pointer)
- Added configurable options for played and resume times/percentages (null_pointer)
- Added extra information for RecentMovies/Episodes (im85288)
- Added RecentAlbums, RandomMovies, RandomEpisodes, RandomAlbums, NextUpTV services (im85288)
- Provide runtime and other information in list view (xnappo)
- Added 'Studio' metadata (xnappo)
- Added 'poster' art (xnappo)
- Added BoxSet video node (xnappo)
- Added trailers count, fixed movie totals (im85288)
0.7.5 - Added simplejson/json switch
- Added simplejson as a requirement
- Changed to use 'Type' instead of 'DisplayMediaType' per Luke
- Added Confluence skin mods (null_pointer)
- Added recentmovie/recenttv list for use by skins (null_pointer)
- Bug fix in service to use data from settings
- Make using Series art for episodes an option
0.7.0 - Switched all data from XML to JSON
- NOTE: If you have added nodes to your main menu, you will need to redo them
- Removed local image copying - new image proxy service by Null_Pointer!
- NOTE: You can delete the .png files in addon_data!
- Added local data cache (null_pointer)
- Changed 'Play All From Here' to start from current episode
- Fixed crash in latest episodes when a 'special' is present
- Fixed DVD playback
0.6.5 - Added preliminary transcoding support
- Added preliminary music support (plays, no metadata yet)
- Fixed bug with non-ASCII characters in collection name
- Gracefully handle username not specified
- Fixed XML compliance issue for official repo submission
0.6.0 - Added resume tracking
- Added playback from resume point (SMB only)
- Added support for multiple users
- Added password authentication
- Added SMB username/password option
- Added option to play from HTTP instead of SMB (note: resume does not work with this option)
- Added default sort modes
- Changed to not resolve real path until playback. Pi speedup?
- Fixed boxsets containing only one movie
- Removed xml caching - not needed (switched from httplib2 to requests)
- Cleaned up more for official repo submission requirements
0.5.5 - Finished requirements for official repo submission
- Added localization
- Added 'Auto enter single folder items' option
- Added 'Play from here'
- Added Genre filter to context menu
- Added 'NextUp' menu entry
0.5.0 - Added Sorting support via Context Menu
- Added Sort order support via Context Menu
- Fixed bug with unaired shows appearing in TV
- Fixed bug with certain characters causing errors in playback path
0.4.5 - Added Recently Added Movies, TV
- Added Favorites support (excuse the trophy icon instead of heart, best I could do)
- Added Upcoming TV
- Added option to mark watched on play (still not progress tracking)
- Preparing for official repository submission (dos2unix lfs)
- Made context menu smarter
- Use Show art for Episodes (for now - MB3 episode artwork doesn't play well will XBMC skins)
- Changed cache to default to 0 (off) - this was needed only because of a FlexRaid issue on my system
0.4.0 - Added section title
- Display correct list type for category
- Implemented context menus for delete/mark watched/mark unwatched.
- Added episode numbers
- Added cast info
0.3.0 - Fixes boxsets
- Added meta-data
0.2.0 - Added caching
- Removed more plex stuff
- XBMB3C-specific settings
0.1.0 - Initial release

2897
default.py Normal file

File diff suppressed because it is too large Load Diff

BIN
icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

1
resources/__init__.py Normal file
View File

@ -0,0 +1 @@
# Dummy file to make this directory a package.

View File

@ -0,0 +1,194 @@
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<strings>
<string id="30000">Primary Server Address</string>
<string id="30001">Auto enter single folder items:</string>
<string id="30002">Play from HTTP instead of SMB:</string>
<string id="30004">Log Level:</string>
<string id="30005">Username: </string>
<string id="30006">Password: </string>
<string id="30007">Samba Username: </string>
<string id="30008">Samba Password: </string>
<string id="30009">Transcode: </string>
<string id="30010">Enable performance profiling</string>
<string id="30014">MediaBrowser</string>
<string id="30015">Network</string>
<string id="30016">Device Name</string>
<string id="30022">Advanced</string>
<string id="30024">Username:</string>
<string id="30025">Password:</string>
<string id="30026">Use SIMPLEJSON instead of JSON</string>
<string id="30030">Port Number:</string>
<string id="30036">Number of recent Movies to show:</string>
<string id="30037">Number of recent TV episodes to show:</string>
<string id="30035">Number of recent Music Albums to show:</string>
<string id="30038">Mark watched at start of playback:</string>
<string id="30039">Set Season poster for episodes</string>
<string id="30040">Genre Filter ...</string>
<string id="30041">Play All from Here</string>
<string id="30042">Refresh</string>
<string id="30043">Delete</string>
<string id="30046">Add Movie to CouchPotato</string>
<string id="30044">Incorrect Username/Password</string>
<string id="30045">Username not found</string>
<string id="30052">Deleting</string>
<string id="30053">Waiting for server to delete</string>
<string id="30059">Server Default</string>
<string id="30060">Title</string>
<string id="30061">Year</string>
<string id="30062">Premiere Date</string>
<string id="30063">Date Created</string>
<string id="30064">Critic Rating</string>
<string id="30065">Community Rating</string>
<string id="30066">Play Count</string>
<string id="30067">Budget</string>
<string id="30068">Sort By</string>
<string id="30069">None</string>
<string id="30070">Action</string>
<string id="30071">Adventure</string>
<string id="30072">Animation</string>
<string id="30073">Crime</string>
<string id="30074">Comedy</string>
<string id="30075">Documentary</string>
<string id="30076">Drama</string>
<string id="30077">Fantasy</string>
<string id="30078">Foreign</string>
<string id="30079">History</string>
<string id="30080">Horror</string>
<string id="30081">Music</string>
<string id="30082">Musical</string>
<string id="30083">Mystery</string>
<string id="30084">Romance</string>
<string id="30085">Science Fiction</string>
<string id="30086">Short</string>
<string id="30087">Suspense</string>
<string id="30088">Thriller</string>
<string id="30089">Western</string>
<string id="30090">Genre Filter</string>
<string id="30091">Confirm file delete?</string>
<string id="30092">Delete this item? This action will delete media and associated data files.</string>
<string id="30093">Mark Watched</string>
<string id="30094">Mark Unwatched</string>
<string id="30095">Add to Favorites</string>
<string id="30096">Remove from Favorites</string>
<string id="30097">Sort By ...</string>
<string id="30098">Sort Order Descending</string>
<string id="30099">Sort Order Ascending</string>
<string id="30100">Show People</string>
<string id="30110">Interface</string>
<string id="30111">Include Stream Info</string>
<string id="30112">Include People</string>
<string id="30113">Include Overview</string>
<string id="30114">On Resume Jump Back Seconds</string>
<string id="30115">Mark Played When Stopping Above %</string>
<string id="30116">Add Item and Played Counts</string>
<string id="30117"> - Background Art Refresh Rate (seconds)</string>
<string id="30118">Add Resume Percent</string>
<string id="30119">Add Episode Number</string>
<string id="30120">Show Load Progress</string>
<string id="30121">Loading Content</string>
<string id="30122">Retrieving Data</string>
<string id="30123">Parsing Jason Data</string>
<string id="30124">Downloading Jason Data</string>
<string id="30125">Done</string>
<string id="30126">Processing Item : </string>
<string id="30127">Offer delete for watched episodes</string>
<string id="30128">Play Error</string>
<string id="30129">This item is not playable</string>
<string id="30130">Local path detected</string>
<string id="30131">Your MB3 Server contains local paths. Please change server paths to UNC or change XBMB3C setting 'Play from Stream' to true. Path: </string>
<string id="30132">Warning</string>
<string id="30133">Debug logging enabled.</string>
<string id="30134">This will affect performance.</string>
<string id="30135">Error</string>
<string id="30136">XBMB3C service is not running</string>
<string id="30137">Please restart XBMC</string>
<string id="30138">Search</string>
<string id="30139">Enable Theme Music (Requires Restart)</string>
<string id="30140"> - Loop Theme Music</string>
<string id="30141">Enable Background Image (Requires Restart)</string>
<string id="30142">Services</string>
<string id="30143">Enable Info Loader (Requires Restart)</string>
<string id="30144">Enable Menu Loader (Requires Restart)</string>
<string id="30145">Enable WebSocket Remote (Requires Restart)</string>
<string id="30146">Enable In Progress Loader (Requires Restart)</string>
<string id="30147">Enable Recent Info Loader (Requires Restart)</string>
<string id="30148">Enable Random Loader (Requires Restart)</string>
<string id="30149">Enable Next Up Loader (Requires Restart)</string>
<string id="30150">Skin does not support setting views</string>
<string id="30151">Select item action (Requires Restart)</string>
<string id="30152">Show Indicators</string>
<string id="30153"> - Show Watched Indicator</string>
<string id="30154"> - Show Unplayed Count Indicator</string>
<string id="30155"> - Show Played Percentage Indicator</string>
<string id="30156">Sort NextUp by Show Title</string>
<string id="30157">Disable Enhanced Images (eg CoverArt)</string>
<string id="30158">Metadata</string>
<string id="30159">Artwork</string>
<string id="30160">Video Quality</string>
<string id="30161">Enable Suggested Loader (Requires Restart)</string>
<string id="30162">Add Season Number</string>
<string id="30163">Flatten Seasons</string>
<string id="30164">Direct Play - HTTP</string>
<string id="30165">Direct Play</string>
<string id="30166">Transcoding</string>
<string id="30167">Server Detection Succeeded</string>
<string id="30168">Found server</string>
<string id="30169">Address : </string>
<string id="30170">All Movies</string>
<string id="30171">All TV</string>
<string id="30172">All Music</string>
<string id="30173">Channels</string>
<string id="30174">Recently Added Movies</string>
<string id="30175">Recently Added Episodes</string>
<string id="30176">Recently Added Albums</string>
<string id="30177">In Progress Movies</string>
<string id="30178">In Progress Episodes</string>
<string id="30179">Next Episodes</string>
<string id="30180">Favorite Movies</string>
<string id="30181">Favorite Shows</string>
<string id="30182">Favorite Episodes</string>
<string id="30183">Frequent Played Albums</string>
<string id="30184">Upcoming TV</string>
<string id="30185">BoxSets</string>
<string id="30186">Trailers</string>
<string id="30187">Music Videos</string>
<string id="30188">Photos</string>
<string id="30189">Unwatched Movies</string>
<string id="30190">Movie Genres</string>
<string id="30191">Movie Studios</string>
<string id="30192">Movie Actors</string>
<string id="30193">Unwatched Episodes</string>
<string id="30194">TV Genres</string>
<string id="30195">TV Networks</string>
<string id="30196">TV Actors</string>
<string id="30197">Playlists</string>
<string id="30198">Search</string>
<string id="30199">Set Views</string>
<string id="30200">Select User</string>
<string id="30201">Profiling enabled.</string>
<string id="30202">Please remember to turn off when finished testing.</string>
<string id="30203">Error in ArtworkRotationThread</string>
<string id="30204">Unable to connect to host</string>
<string id="30205">Error in LoadMenuOptionsThread</string>
</strings>

View File

@ -0,0 +1,809 @@
#################################################################################################
# Start of BackgroundRotationThread
# Sets a backgound property to a fan art link
#################################################################################################
import xbmc
import xbmcgui
import xbmcaddon
import json
import threading
from datetime import datetime
import urllib
import urllib2
import random
import time
from DownloadUtils import DownloadUtils
_MODE_BASICPLAY=12
#define our global download utils
downloadUtils = DownloadUtils()
class ArtworkRotationThread(threading.Thread):
movie_art_links = []
tv_art_links = []
music_art_links = []
global_art_links = []
item_art_links = {}
current_movie_art = 0
current_tv_art = 0
current_music_art = 0
current_global_art = 0
current_item_art = 0
linksLoaded = False
logLevel = 0
currentFilteredIndex = {}
def __init__(self, *args):
addonSettings = xbmcaddon.Addon(id='plugin.video.xbmb3c')
self.addonSettings = xbmcaddon.Addon(id='plugin.video.xbmb3c')
self.getString = self.addonSettings.getLocalizedString
level = addonSettings.getSetting('logLevel')
self.logLevel = 0
if(level != None):
self.logLevel = int(level)
xbmc.log("XBMB3C BackgroundRotationThread -> Log Level:" + str(self.logLevel))
threading.Thread.__init__(self, *args)
def logMsg(self, msg, level = 1):
if(self.logLevel >= level):
xbmc.log("XBMB3C BackgroundRotationThread -> " + msg)
def run(self):
try:
self.run_internal()
except Exception, e:
xbmcgui.Dialog().ok(self.getString(30203), str(e))
raise
def run_internal(self):
self.logMsg("Started")
try:
self.loadLastBackground()
except Exception, e:
self.logMsg("loadLastBackground Exception : " + str(e), level=0)
WINDOW = xbmcgui.Window( 10000 )
filterOnParent_Last = WINDOW.getProperty("MB3.Background.Collection")
last_id = ""
self.updateArtLinks()
#self.setBackgroundLink(filterOnParent_Last)
lastRun = datetime.today()
itemLastRun = datetime.today()
addonSettings = xbmcaddon.Addon(id='plugin.video.xbmb3c')
backgroundRefresh = int(addonSettings.getSetting('backgroundRefresh'))
if(backgroundRefresh < 10):
backgroundRefresh = 10
itemBackgroundRefresh = 5
lastUserName = addonSettings.getSetting('username')
while (xbmc.abortRequested == False):
td = datetime.today() - lastRun
td2 = datetime.today() - itemLastRun
secTotal = td.seconds
secTotal2 = td2.seconds
userName = addonSettings.getSetting('username')
self.logMsg("Server details string : (" + userName + ") (" + lastUserName + ")", level=2)
Collection = WINDOW.getProperty("MB3.Background.Collection")
if(secTotal > backgroundRefresh or filterOnParent_Last != Collection or userName != lastUserName):
lastUserName = userName
if(self.linksLoaded == False):
self.updateArtLinks()
lastRun = datetime.today()
filterOnParent_Last = Collection
backgroundRefresh = int(addonSettings.getSetting('backgroundRefresh'))
self.setBackgroundLink(Collection)
if(backgroundRefresh < 10):
backgroundRefresh = 10
# update item BG every 7 seconds
if(secTotal2 > itemBackgroundRefresh):
self.setItemBackgroundLink()
itemLastRun = datetime.today()
# update item BG on selected item changes
if xbmc.getInfoLabel('ListItem.Property(id)') != None:
current_id = xbmc.getInfoLabel('ListItem.Property(id)')
elif xbmc.getInfoLabel('ListItem.Property(ItemGUID)') != None:
current_id=xbmc.getInfoLabel('ListItem.Property(ItemGUID)')
else:
current_id = ''
if current_id != last_id:
self.setItemBackgroundLink()
itemLastRun = datetime.today()
last_id = current_id
xbmc.sleep(1000)
try:
self.saveLastBackground()
except Exception, e:
self.logMsg("saveLastBackground Exception : " + str(e), level=0)
self.logMsg("Exited")
def loadLastBackground(self):
__addon__ = xbmcaddon.Addon(id='plugin.video.xbmb3c')
__addondir__ = xbmc.translatePath( __addon__.getAddonInfo('profile') )
lastDataPath = __addondir__ + "LastBgLinks.json"
dataFile = open(lastDataPath, 'r')
jsonData = dataFile.read()
dataFile.close()
self.logMsg(jsonData)
result = json.loads(jsonData)
WINDOW = xbmcgui.Window( 10000 )
if(result.get("global") != None):
WINDOW.setProperty("MB3.Background.Global.FanArt", result.get("global")["url"])
self.logMsg("MB3.Background.Global.FanArt=" + result.get("global")["url"], level=2)
WINDOW.setProperty("MB3.Background.Global.FanArt.Poster", result.get("global")["poster"])
self.logMsg("MB3.Background.Global.FanArt.Poster=" + result.get("global")["poster"], level=2)
WINDOW.setProperty("MB3.Background.Global.FanArt.Action", result.get("global")["action"])
self.logMsg("MB3.Background.Global.FanArt.Action=" + result.get("global")["action"], level=2)
if(result.get("movie") != None):
self.logMsg("Setting Movie Last : " + str(result.get("movie")), level=2)
WINDOW.setProperty("MB3.Background.Movie.FanArt", result.get("movie")["url"])
if(result.get("tv") != None):
self.logMsg("Setting TV Last : " + str(result.get("tv")), level=2)
WINDOW.setProperty("MB3.Background.TV.FanArt", result.get("tv")["url"])
if(result.get("music") != None):
self.logMsg("Setting Music Last : " + str(result.get("music")), level=2)
WINDOW.setProperty("MB3.Background.Music.FanArt", result.get("music")["url"])
def saveLastBackground(self):
data = {}
if(len(self.global_art_links) > 0):
data["global"] = self.global_art_links[self.current_global_art]
if(len(self.movie_art_links) > 0):
data["movie"] = self.movie_art_links[self.current_movie_art]
if(len(self.tv_art_links) > 0):
data["tv"] = self.tv_art_links[self.current_tv_art]
if(len(self.music_art_links) > 0):
data["music"] = self.music_art_links[self.current_music_art]
__addon__ = xbmcaddon.Addon(id='plugin.video.xbmb3c')
__addondir__ = xbmc.translatePath( __addon__.getAddonInfo('profile') )
lastDataPath = __addondir__ + "LastBgLinks.json"
dataFile = open(lastDataPath, 'w')
stringdata = json.dumps(data)
self.logMsg("Last Background Links : " + stringdata)
dataFile.write(stringdata)
dataFile.close()
def setBackgroundLink(self, filterOnParent):
WINDOW = xbmcgui.Window( 10000 )
if(len(self.movie_art_links) > 0):
self.logMsg("setBackgroundLink index movie_art_links " + str(self.current_movie_art + 1) + " of " + str(len(self.movie_art_links)), level=2)
artUrl = self.movie_art_links[self.current_movie_art]["url"]
WINDOW.setProperty("MB3.Background.Movie.FanArt", artUrl)
self.logMsg("MB3.Background.Movie.FanArt=" + artUrl)
self.current_movie_art = self.current_movie_art + 1
if(self.current_movie_art == len(self.movie_art_links)):
self.current_movie_art = 0
if(len(self.tv_art_links) > 0):
self.logMsg("setBackgroundLink index tv_art_links " + str(self.current_tv_art + 1) + " of " + str(len(self.tv_art_links)), level=2)
artUrl = self.tv_art_links[self.current_tv_art]["url"]
WINDOW.setProperty("MB3.Background.TV.FanArt", artUrl)
self.logMsg("MB3.Background.TV.FanArt=" + artUrl)
self.current_tv_art = self.current_tv_art + 1
if(self.current_tv_art == len(self.tv_art_links)):
self.current_tv_art = 0
if(len(self.music_art_links) > 0):
self.logMsg("setBackgroundLink index music_art_links " + str(self.current_music_art + 1) + " of " + str(len(self.music_art_links)), level=2)
artUrl = self.music_art_links[self.current_music_art]["url"]
WINDOW.setProperty("MB3.Background.Music.FanArt", artUrl)
self.logMsg("MB3.Background.Music.FanArt=" + artUrl)
self.current_music_art = self.current_music_art + 1
if(self.current_music_art == len(self.music_art_links)):
self.current_music_art = 0
if(len(self.global_art_links) > 0):
self.logMsg("setBackgroundLink index global_art_links " + str(self.current_global_art + 1) + " of " + str(len(self.global_art_links)), level=2)
next, nextItem = self.findNextLink(self.global_art_links, self.current_global_art, filterOnParent)
#nextItem = self.global_art_links[self.current_global_art]
self.current_global_art = next
backGroundUrl = nextItem["url"]
posterUrl = nextItem["poster"]
actionUrl = nextItem["action"]
addonSettings = xbmcaddon.Addon(id='plugin.video.xbmb3c')
selectAction = addonSettings.getSetting('selectAction')
if(selectAction == "1"):
actionUrl = "RunPlugin(plugin://plugin.video.xbmb3c/?id=" + nextItem["id"] + "&mode=17)"
else:
actionUrl = nextItem["action"]
WINDOW.setProperty("MB3.Background.Global.FanArt", backGroundUrl)
self.logMsg("MB3.Background.Global.FanArt=" + backGroundUrl)
WINDOW.setProperty("MB3.Background.Global.FanArt.Poster", posterUrl)
self.logMsg("MB3.Background.Global.FanArt.Poster=" + posterUrl)
WINDOW.setProperty("MB3.Background.Global.FanArt.Action", actionUrl)
self.logMsg("MB3.Background.Global.FanArt.Action=" + actionUrl)
def findNextLink(self, linkList, startIndex, filterOnParent):
if(filterOnParent == None or filterOnParent == ""):
filterOnParent = "empty"
addonSettings = xbmcaddon.Addon(id='plugin.video.xbmb3c')
backgroundRefresh = int(addonSettings.getSetting('backgroundRefresh'))
if(backgroundRefresh < 10):
backgroundRefresh = 10
# first check the cache if we are filtering
if(self.currentFilteredIndex.get(filterOnParent) != None):
cachedItem = self.currentFilteredIndex.get(filterOnParent)
self.logMsg("filterOnParent=existing=" + filterOnParent + "=" + str(cachedItem))
cachedIndex = cachedItem[0]
dateStamp = cachedItem[1]
td = datetime.today() - dateStamp
secTotal = td.seconds
if(secTotal < backgroundRefresh):
# use the cached background index
self.logMsg("filterOnParent=using=" + filterOnParent + "=" + str(secTotal))
return (cachedIndex, linkList[cachedIndex])
currentIndex = startIndex
isParentMatch = False
#xbmc.log("findNextLink : filterOnParent=" + str(filterOnParent) + " isParentMatch=" + str(isParentMatch))
while(isParentMatch == False):
currentIndex = currentIndex + 1
if(currentIndex == len(linkList)):
currentIndex = 0
if(currentIndex == startIndex):
return (currentIndex, linkList[currentIndex]) # we checked everything and nothing was ok so return the first one again
isParentMatch = True
# if filter on not empty then make sure we have a bg from the correct collection
if(filterOnParent != "empty"):
isParentMatch = filterOnParent in linkList[currentIndex]["collections"]
# save the cached index
cachedItem = [currentIndex, datetime.today()]
self.logMsg("filterOnParent=adding=" + filterOnParent + "=" + str(cachedItem))
self.currentFilteredIndex[filterOnParent] = cachedItem
nextIndex = currentIndex + 1
if(nextIndex == len(linkList)):
nextIndex = 0
return (nextIndex, linkList[currentIndex])
def updateArtLinks(self):
t1 = time.time()
result01 = self.updateCollectionArtLinks()
t2 = time.time()
result02 = self.updateTypeArtLinks()
t3 = time.time()
diff = t2 - t1
xbmc.log("TIMEDIFF01 : " + str(diff))
diff = t3 - t2
xbmc.log("TIMEDIFF02 : " + str(diff))
if(result01 and result02):
xbmc.log("BackgroundRotationThread Update Links Worked")
self.linksLoaded = True
else:
xbmc.log("BackgroundRotationThread Update Links Failed")
self.linksLoaded = False
def updateActionUrls(self):
xbmc.log("BackgroundRotationThread updateActionUrls Called")
WINDOW = xbmcgui.Window( 10000 )
for x in range(0, 10):
contentUrl = WINDOW.getProperty("xbmb3c_collection_menuitem_content_" + str(x))
if(contentUrl != None):
index = contentUrl.find("SessionId=(")
if(index > -1):
index = index + 11
index2 = contentUrl.find(")", index+1)
timeNow = time.time()
newContentUrl = contentUrl[:index] + str(timeNow) + contentUrl[index2:]
xbmc.log("xbmb3c_collection_menuitem_content_" + str(x) + "=" + newContentUrl)
WINDOW.setProperty("xbmb3c_collection_menuitem_content_" + str(x), newContentUrl)
def updateCollectionArtLinks(self):
self.logMsg("updateCollectionArtLinks Called")
addonSettings = xbmcaddon.Addon(id='plugin.video.xbmb3c')
mb3Host = addonSettings.getSetting('ipaddress')
mb3Port = addonSettings.getSetting('port')
userName = addonSettings.getSetting('username')
# get the user ID
userid = downloadUtils.getUserId()
self.logMsg("updateCollectionArtLinks UserID : " + userid)
userUrl = "http://" + mb3Host + ":" + mb3Port + "/mediabrowser/Users/" + userid + "/Items/Root?format=json"
jsonData = downloadUtils.downloadUrl(userUrl, suppress=False, popup=1 )
self.logMsg("updateCollectionArtLinks UserData : " + str(jsonData), 2)
result = json.loads(jsonData)
parentid = result.get("Id")
self.logMsg("updateCollectionArtLinks ParentID : " + str(parentid), 2)
userRootPath = "http://" + mb3Host + ":" + mb3Port + "/mediabrowser/Users/" + userid + "/items?ParentId=" + parentid + "&SortBy=SortName&Fields=CollectionType,Overview,RecursiveItemCount&format=json"
jsonData = downloadUtils.downloadUrl(userRootPath, suppress=False, popup=1 )
self.logMsg("updateCollectionArtLinks userRootPath : " + str(jsonData), 2)
result = json.loads(jsonData)
result = result.get("Items")
artLinks = {}
collection_count = 0
WINDOW = xbmcgui.Window( 10000 )
# process collections
for item in result:
collectionType = item.get("CollectionType", "")
name = item.get("Name")
childCount = item.get("RecursiveItemCount")
self.logMsg("updateCollectionArtLinks Name : " + name, level=1)
self.logMsg("updateCollectionArtLinks RecursiveItemCount : " + str(childCount), level=1)
if(childCount == None or childCount == 0):
continue
self.logMsg("updateCollectionArtLinks Processing Collection : " + name + " of type : " + collectionType, level=2)
#####################################################################################################
# Process collection item menu item
timeNow = time.time()
contentUrl = "plugin://plugin.video.xbmb3c?mode=16&ParentId=" + item.get("Id") + "&CollectionType=" + collectionType + "&SessionId=(" + str(timeNow) + ")"
actionUrl = ("ActivateWindow(VideoLibrary, plugin://plugin.video.xbmb3c/?mode=21&ParentId=" + item.get("Id") + "&Name=" + name + ",return)").encode('utf-8')
xbmc.log("COLLECTION actionUrl: " + actionUrl)
WINDOW.setProperty("xbmb3c_collection_menuitem_name_" + str(collection_count), name)
WINDOW.setProperty("xbmb3c_collection_menuitem_action_" + str(collection_count), actionUrl)
WINDOW.setProperty("xbmb3c_collection_menuitem_collection_" + str(collection_count), name)
WINDOW.setProperty("xbmb3c_collection_menuitem_content_" + str(collection_count), contentUrl)
#####################################################################################################
#####################################################################################################
# Process collection item backgrounds
collectionUrl = "http://" + mb3Host + ":" + mb3Port + "/mediabrowser/Users/" + userid + "/items?ParentId=" + item.get("Id") + "&IncludeItemTypes=Movie,Series,Episode,MusicArtist,Trailer,MusicVideo,Video&Fields=ParentId,Overview&Recursive=true&CollapseBoxSetItems=false&format=json"
jsonData = downloadUtils.downloadUrl(collectionUrl, suppress=False, popup=1 )
collectionResult = json.loads(jsonData)
collectionResult = collectionResult.get("Items")
if(collectionResult == None):
collectionResult = []
for col_item in collectionResult:
id = col_item.get("Id")
name = col_item.get("Name")
images = col_item.get("BackdropImageTags")
if(images != None and len(images) > 0):
stored_item = artLinks.get(id)
if(stored_item == None):
stored_item = {}
collections = []
collections.append(item.get("Name"))
stored_item["collections"] = collections
links = []
images = col_item.get("BackdropImageTags")
parentID = col_item.get("ParentId")
name = col_item.get("Name")
if (images == None):
images = []
index = 0
# build poster image link
posterImage = ""
actionUrl = ""
if(col_item.get("Type") == "Movie" or col_item.get("Type") == "Trailer" or col_item.get("Type") == "MusicVideo" or col_item.get("Type") == "Video"):
posterImage = downloadUtils.getArtwork(col_item, "Primary")
url = mb3Host + ":" + mb3Port + ',;' + id
url = urllib.quote(url)
#actionUrl = "ActivateWindow(VideoLibrary, plugin://plugin.video.xbmb3c/?mode=" + str(_MODE_BASICPLAY) + "&url=" + url + " ,return)"
actionUrl = "RunPlugin(plugin://plugin.video.xbmb3c/?mode=" + str(_MODE_BASICPLAY) + "&url=" + url + ")"
elif(col_item.get("Type") == "Series"):
posterImage = downloadUtils.getArtwork(col_item, "Primary")
actionUrl = "ActivateWindow(VideoLibrary, plugin://plugin.video.xbmb3c/?mode=21&ParentId=" + id + "&Name=" + name + ",return)"
plot = col_item.get("Overview")
for backdrop in images:
info = {}
info["url"] = downloadUtils.getArtwork(col_item, "Backdrop", index=str(index))
info["poster"] = posterImage
info["action"] = actionUrl
info["index"] = index
info["id"] = id
info["action"] = "None"
info["plot"] = plot
info["parent"] = parentID
info["name"] = name
links.append(info)
index = index + 1
stored_item["links"] = links
artLinks[id] = stored_item
else:
stored_item["collections"].append(item.get("Name"))
#####################################################################################################
collection_count = collection_count + 1
# build global link list
final_global_art = []
for id in artLinks:
item = artLinks.get(id)
collections = item.get("collections")
links = item.get("links")
for link_item in links:
link_item["collections"] = collections
final_global_art.append(link_item)
#xbmc.log("COLLECTION_DATA GROUPS " + str(link_item))
self.global_art_links = final_global_art
random.shuffle(self.global_art_links)
self.logMsg("Background Global Art Links : " + str(len(self.global_art_links)))
return True
def updateTypeArtLinks(self):
self.logMsg("updateTypeArtLinks Called")
addonSettings = xbmcaddon.Addon(id='plugin.video.xbmb3c')
mb3Host = addonSettings.getSetting('ipaddress')
mb3Port = addonSettings.getSetting('port')
userName = addonSettings.getSetting('username')
# get the user ID
userid = downloadUtils.getUserId()
self.logMsg("updateTypeArtLinks UserID : " + userid)
# load Movie BG
moviesUrl = "http://" + mb3Host + ":" + mb3Port + "/mediabrowser/Users/" + userid + "/Items?Fields=ParentId,Overview&CollapseBoxSetItems=false&Recursive=true&IncludeItemTypes=Movie&format=json"
jsonData = downloadUtils.downloadUrl(moviesUrl, suppress=False, popup=1 )
result = json.loads(jsonData)
result = result.get("Items")
if(result == None):
result = []
for item in result:
images = item.get("BackdropImageTags")
id = item.get("Id")
parentID = item.get("ParentId")
name = item.get("Name")
plot = item.get("Overview")
url = mb3Host + ":" + mb3Port + ',;' + id
url = urllib.quote(url)
actionUrl = "RunPlugin(plugin://plugin.video.xbmb3c/?mode=" + str(_MODE_BASICPLAY) + "&url=" + url + ")"
if (images == None):
images = []
index = 0
trailerActionUrl = None
if item.get("LocalTrailerCount") != None and item.get("LocalTrailerCount") > 0:
itemTrailerUrl = "http://" + mb3Host + ":" + mb3Port + "/mediabrowser/Users/" + userid + "/Items/" + id + "/LocalTrailers?format=json"
jsonData = downloadUtils.downloadUrl(itemTrailerUrl, suppress=False, popup=1 )
trailerItem = json.loads(jsonData)
trailerUrl = mb3Host + ":" + mb3Port + ',;' + trailerItem[0].get("Id")
trailerUrl = urllib.quote(trailerUrl)
trailerActionUrl = "RunPlugin(plugin://plugin.video.xbmb3c/?mode=" + str(_MODE_BASICPLAY) + "&url=" + trailerUrl + ")"
for backdrop in images:
info = {}
info["url"] = downloadUtils.getArtwork(item, "Backdrop", index=str(index))
info["index"] = index
info["id"] = id
info["plot"] = plot
info["action"] = actionUrl
info["trailer"] = trailerActionUrl
info["parent"] = parentID
info["name"] = name
self.logMsg("BG Movie Image Info : " + str(info), level=2)
if (info not in self.movie_art_links):
self.movie_art_links.append(info)
index = index + 1
random.shuffle(self.movie_art_links)
self.logMsg("Background Movie Art Links : " + str(len(self.movie_art_links)))
# load TV BG links
tvUrl = "http://" + mb3Host + ":" + mb3Port + "/mediabrowser/Users/" + userid + "/Items?Fields=ParentId,Overview&CollapseBoxSetItems=false&Recursive=true&IncludeItemTypes=Series&format=json"
jsonData = downloadUtils.downloadUrl(tvUrl, suppress=False, popup=1 )
result = json.loads(jsonData)
result = result.get("Items")
if(result == None):
result = []
for item in result:
images = item.get("BackdropImageTags")
id = item.get("Id")
parentID = item.get("ParentId")
name = item.get("Name")
plot = item.get("Overview")
if (images == None):
images = []
index = 0
for backdrop in images:
info = {}
info["url"] = downloadUtils.getArtwork(item, "Backdrop", index=str(index))
info["index"] = index
info["id"] = id
info["action"] = "None"
info["trailer"] = "None"
info["plot"] = plot
info["parent"] = parentID
info["name"] = name
self.logMsg("BG TV Image Info : " + str(info), level=2)
if (info not in self.tv_art_links):
self.tv_art_links.append(info)
index = index + 1
random.shuffle(self.tv_art_links)
self.logMsg("Background Tv Art Links : " + str(len(self.tv_art_links)))
# load music BG links
musicUrl = "http://" + mb3Host + ":" + mb3Port + "/mediabrowser/Users/" + userid + "/Items?Fields=ParentId,Overview&CollapseBoxSetItems=false&Recursive=true&IncludeItemTypes=MusicArtist&format=json"
jsonData = downloadUtils.downloadUrl(musicUrl, suppress=False, popup=1 )
result = json.loads(jsonData)
result = result.get("Items")
if(result == None):
result = []
for item in result:
images = item.get("BackdropImageTags")
id = item.get("Id")
parentID = item.get("ParentId")
name = item.get("Name")
plot = item.get("Overview")
if (images == None):
images = []
index = 0
for backdrop in images:
info = {}
info["url"] = downloadUtils.getArtwork(item, "Backdrop", index=str(index))
info["index"] = index
info["id"] = id
info["action"] = "None"
info["trailer"] = "None"
info["plot"] = plot
info["parent"] = parentID
info["name"] = name
self.logMsg("BG Music Image Info : " + str(info), level=2)
if (info not in self.music_art_links):
self.music_art_links.append(info)
index = index + 1
random.shuffle(self.music_art_links)
self.logMsg("Background Music Art Links : " + str(len(self.music_art_links)))
#
# build a map indexed by id that contains a list of BG art for each item
# this is used for selected item BG rotation
#
self.item_art_links = {}
# add movie BG links
for bg_item in self.movie_art_links:
item_id = bg_item["id"]
if(self.item_art_links.get(item_id) != None):
self.item_art_links[item_id].append(bg_item)
else:
bg_list = []
bg_list.append(bg_item)
self.item_art_links[item_id] = bg_list
# add TV BG links
for bg_item in self.tv_art_links:
item_id = bg_item["id"]
if(self.item_art_links.get(item_id) != None):
self.item_art_links[item_id].append(bg_item)
else:
bg_list = []
bg_list.append(bg_item)
self.item_art_links[item_id] = bg_list
# add music BG links
for bg_item in self.music_art_links:
item_id = bg_item["id"]
if(self.item_art_links.get(item_id) != None):
self.item_art_links[item_id].append(bg_item)
else:
bg_list = []
bg_list.append(bg_item)
self.item_art_links[item_id] = bg_list
return True
def setItemBackgroundLink(self):
id = xbmc.getInfoLabel('ListItem.Property(ItemGUID)')
self.logMsg("setItemBackgroundLink ItemGUID : " + id, 1)
WINDOW = xbmcgui.Window( 10000 )
if id != None and id != "":
listOfBackgrounds = self.item_art_links.get(id)
listOfData = self.item_art_links.get(xbmc.getInfoLabel('ListItem.Property(id)'))
# if for some reson the item is not in the cache try to load it now
if(listOfBackgrounds == None or len(listOfBackgrounds) == 0):
self.loadItemBackgroundLinks(id)
if(listOfData == None or len(listOfData) == 0):
self.loadItemBackgroundLinks(xbmc.getInfoLabel('ListItem.Property(id)'))
listOfBackgrounds = self.item_art_links.get(id)
listOfData = self.item_art_links.get(xbmc.getInfoLabel('ListItem.Property(id)'))
if listOfBackgrounds != None:
if listOfData != None:
if listOfData[0]["plot"] != "" and listOfData[0]["plot"] != None:
plot=listOfData[0]["plot"]
plot=plot.encode("utf-8")
WINDOW.setProperty("MB3.Plot", plot )
else:
WINDOW.clearProperty("MB3.Plot")
if listOfBackgrounds[0]["action"] != None and listOfBackgrounds[0]["action"] != "":
action=listOfBackgrounds[0]["action"]
WINDOW.setProperty("MB3.Action", action )
else:
WINDOW.clearProperty("MB3.Action")
if listOfBackgrounds[0].get("trailer") != None and listOfBackgrounds[0]["trailer"] != "":
trailerAction=listOfBackgrounds[0]["trailer"]
WINDOW.setProperty("MB3.TrailerAction", trailerAction )
else:
WINDOW.clearProperty("MB3.TrailerAction")
if(listOfBackgrounds != None and len(listOfBackgrounds) > 0):
self.logMsg("setItemBackgroundLink Image " + str(self.current_item_art + 1) + " of " + str(len(listOfBackgrounds)), 1)
try:
artUrl = listOfBackgrounds[self.current_item_art]["url"]
except IndexError:
self.current_item_art = 0
artUrl = listOfBackgrounds[self.current_item_art]["url"]
WINDOW.setProperty("MB3.Background.Item.FanArt", artUrl)
self.logMsg("setItemBackgroundLink MB3.Background.Item.FanArt=" + artUrl, 1)
self.current_item_art = self.current_item_art + 1
if(self.current_item_art == len(listOfBackgrounds) - 1):
self.current_item_art = 0
else:
self.logMsg("setItemBackgroundLink Resetting MB3.Background.Item.FanArt", 1)
WINDOW.clearProperty("MB3.Background.Item.FanArt")
else:
self.logMsg("setItemBackgroundLink Resetting MB3.Background.Item.FanArt", 1)
WINDOW.clearProperty("MB3.Background.Item.FanArt")
WINDOW.clearProperty("MB3.Plot")
WINDOW.clearProperty("MB3.Action")
WINDOW.clearProperty("MB3.TrailerAction")
def loadItemBackgroundLinks(self, id):
if(id == None or len(id) == 0):
self.logMsg("loadItemBackgroundLinks id was empty")
return
self.logMsg("loadItemBackgroundLinks Called for id : " + id)
addonSettings = xbmcaddon.Addon(id='plugin.video.xbmb3c')
mb3Host = addonSettings.getSetting('ipaddress')
mb3Port = addonSettings.getSetting('port')
userName = addonSettings.getSetting('username')
userid = downloadUtils.getUserId()
itemUrl = "http://" + mb3Host + ":" + mb3Port + "/mediabrowser/Users/" + userid + "/Items/" + id + "?Fields=ParentId,Overview&format=json"
jsonData = downloadUtils.downloadUrl(itemUrl, suppress=False, popup=1 )
item = json.loads(jsonData)
self.logMsg("loadItemBackgroundLinks found item : " + str(item), 2);
if(item == None):
item = []
#for item in result:
images = item.get("BackdropImageTags")
plot = item.get("Overview")
id = item.get("Id")
urlid = id
parentID = item.get("ParentId")
origid = id
name = item.get("Name")
if (images == None or images == []):
images = item.get("ParentBackdropImageTags")
urlid = item.get("ParentBackdropItemId")
if (images == None):
images = []
index = 0
url = mb3Host + ":" + mb3Port + ',;' + id
url = urllib.quote(url)
actionUrl = "RunPlugin(plugin://plugin.video.xbmb3c/?mode=" + str(_MODE_BASICPLAY) + "&url=" + url + ")"
trailerActionUrl = None
if item.get("LocalTrailerCount") != None and item.get("LocalTrailerCount") > 0:
itemTrailerUrl = "http://" + mb3Host + ":" + mb3Port + "/mediabrowser/Users/" + userid + "/Items/" + id + "/LocalTrailers?format=json"
jsonData = downloadUtils.downloadUrl(itemTrailerUrl, suppress=False, popup=1 )
trailerItem = json.loads(jsonData)
trailerUrl = mb3Host + ":" + mb3Port + ',;' + trailerItem[0].get("Id")
trailerUrl = urllib.quote(trailerUrl)
trailerActionUrl = "RunPlugin(plugin://plugin.video.xbmb3c/?mode=" + str(_MODE_BASICPLAY) + "&url=" + trailerUrl + ")"
newBgLinks = []
for backdrop in images:
info = {}
info["url"] = downloadUtils.getArtwork(item, "Backdrop", index=str(index))
info["plot"] = plot
info["action"] = actionUrl
info["trailer"] = trailerActionUrl
info["index"] = index
info["id"] = urlid
info["parent"] = parentID
info["name"] = name
self.logMsg("BG Item Image Info : " + str(info), level=2)
newBgLinks.append(info)
index = index + 1
if(len(newBgLinks) > 0):
self.item_art_links[origid] = newBgLinks

View File

@ -0,0 +1,11 @@
from uuid import getnode as get_mac
import xbmcaddon
class ClientInformation():
def getMachineId(self):
return "%012X"%get_mac()
def getVersion(self):
version = xbmcaddon.Addon(id="plugin.video.xbmb3c").getAddonInfo("version")
return version

View File

@ -0,0 +1,422 @@
import xbmc
import xbmcgui
import xbmcaddon
import urllib
import urllib2
import httplib
import requests
import hashlib
import StringIO
import gzip
import sys
import json as json
from random import randrange
from uuid import getnode as get_mac
from ClientInformation import ClientInformation
class DownloadUtils():
logLevel = 0
addonSettings = None
getString = None
def __init__(self, *args):
self.addonSettings = xbmcaddon.Addon(id='plugin.video.xbmb3c')
self.getString = self.addonSettings.getLocalizedString
level = self.addonSettings.getSetting('logLevel')
self.logLevel = 0
if(level != None):
self.logLevel = int(level)
def logMsg(self, msg, level = 1):
if(self.logLevel >= level):
xbmc.log("XBMB3C DownloadUtils -> " + msg)
def getUserId(self):
port = self.addonSettings.getSetting('port')
host = self.addonSettings.getSetting('ipaddress')
userName = self.addonSettings.getSetting('username')
self.logMsg("Looking for user name: " + userName)
jsonData = None
try:
jsonData = self.downloadUrl(host + ":" + port + "/mediabrowser/Users/Public?format=json")
except Exception, msg:
error = "Get User unable to connect to " + host + ":" + port + " : " + str(msg)
xbmc.log (error)
return ""
self.logMsg("GETUSER_JSONDATA_01:" + str(jsonData))
result = []
try:
result = json.loads(jsonData)
except Exception, e:
self.logMsg("jsonload : " + str(e) + " (" + jsonData + ")", level=1)
return ""
self.logMsg("GETUSER_JSONDATA_02:" + str(result))
userid = ""
secure = False
for user in result:
if(user.get("Name") == userName):
userid = user.get("Id")
self.logMsg("Username Found:" + user.get("Name"))
if(user.get("HasPassword") == True):
secure = True
self.logMsg("Username Is Secure (HasPassword=True)")
break
if(secure):
self.authenticate('http://' + host + ":" + port + "/mediabrowser/Users/AuthenticateByName?format=json")
if userid == "":
return_value = xbmcgui.Dialog().ok(self.getString(30045),self.getString(30045))
sys.exit()
self.logMsg("userid : " + userid)
WINDOW = xbmcgui.Window( 10000 )
WINDOW.setProperty("userid", userid)
return userid
def getMachineId(self):
return "%012X"%get_mac()
def authenticate(self, url):
txt_mac = self.getMachineId()
version = ClientInformation().getVersion()
deviceName = self.addonSettings.getSetting('deviceName')
deviceName = deviceName.replace("\"", "_")
authString = "Mediabrowser Client=\"XBMC\",Device=\"" + deviceName + "\",DeviceId=\"" + txt_mac + "\",Version=\"" + version + "\""
headers = {'Accept-encoding': 'gzip', 'Authorization' : authString}
sha1 = hashlib.sha1(self.addonSettings.getSetting('password'))
resp = requests.post(url, data={'password':sha1.hexdigest(),'Username':self.addonSettings.getSetting('username')}, headers=headers)
code=str(resp.status_code)
result = resp.json()
if result.get("AccessToken") != self.addonSettings.getSetting('AccessToken'):
self.addonSettings.setSetting('AccessToken', result.get("AccessToken"))
if int(code) >= 200 and int(code)<300:
self.logMsg("User Authenticated")
else:
self.logMsg("User NOT Authenticated")
return_value = xbmcgui.Dialog().ok(self.getString(30044), self.getString(30044))
sys.exit()
def getArtwork(self, data, type, index = "0", userParentInfo = False):
id = data.get("Id")
getSeriesData = False
if type == "tvshow.poster": # Change the Id to the series to get the overall series poster
if data.get("Type") == "Season" or data.get("Type")== "Episode":
id = data.get("SeriesId")
getSeriesData = True
elif type == "poster" and data.get("Type") == "Episode" and self.addonSettings.getSetting('useSeasonPoster')=='true': # Change the Id to the Season to get the season poster
id = data.get("SeasonId")
if type == "poster" or type == "tvshow.poster": # Now that the Ids are right, change type to MB3 name
type="Primary"
if data.get("Type") == "Season": # For seasons: primary (poster), thumb and banner get season art, rest series art
if type != "Primary" and type != "Thumb" and type != "Banner":
id = data.get("SeriesId")
getSeriesData = True
if data.get("Type") == "Episode": # For episodes: primary (episode thumb) gets episode art, rest series art.
if type != "Primary":
id = data.get("SeriesId")
getSeriesData = True
# if requested get parent info
if getSeriesData == True and userParentInfo == True:
self.logMsg("Using Parent Info for image link", level=1)
mb3Host = self.addonSettings.getSetting('ipaddress')
mb3Port = self.addonSettings.getSetting('port')
userid = self.getUserId()
seriesJsonData = self.downloadUrl("http://" + mb3Host + ":" + mb3Port + "/mediabrowser/Users/" + userid + "/Items/" + id + "?format=json", suppress=False, popup=1 )
seriesResult = json.loads(seriesJsonData)
data = seriesResult
imageTag = "e3ab56fe27d389446754d0fb04910a34" # a place holder tag, needs to be in this format
originalType = type
if type == "Primary2" or type == "Primary3" or type=="SeriesPrimary":
type = "Primary"
if type == "Backdrop2" or type=="Backdrop3":
type = "Backdrop"
if type == "Thumb2" or type=="Thumb3":
type = "Thumb"
if(data.get("ImageTags") != None and data.get("ImageTags").get(type) != None):
imageTag = data.get("ImageTags").get(type)
query = ""
height = "10000"
width = "10000"
played = "0"
if self.addonSettings.getSetting('showIndicators')=='true': # add watched, unplayedcount and percentage played indicators to posters
if (originalType =="Primary" or originalType =="Backdrop") and data.get("Type") != "Episode":
userData = data.get("UserData")
if originalType =="Backdrop" and index == "0":
totalbackdrops = len(data.get("BackdropImageTags"))
if totalbackdrops != 0:
index = str(randrange(0,totalbackdrops))
if userData != None:
UnWatched = 0 if userData.get("UnplayedItemCount")==None else userData.get("UnplayedItemCount")
if UnWatched <> 0 and self.addonSettings.getSetting('showUnplayedIndicators')=='true':
query = query + "&UnplayedCount=" + str(UnWatched)
if(userData != None and userData.get("Played") == True and self.addonSettings.getSetting('showWatchedIndicators')=='true'):
query = query + "&AddPlayedIndicator=true"
PlayedPercentage = 0 if userData.get("PlayedPercentage")==None else userData.get("PlayedPercentage")
if PlayedPercentage == 0 and userData!=None and userData.get("PlayedPercentage")!=None :
PlayedPercentage = userData.get("PlayedPercentage")
if (PlayedPercentage != 100 or PlayedPercentage) != 0 and self.addonSettings.getSetting('showPlayedPrecentageIndicators')=='true':
played = str(PlayedPercentage)
elif originalType =="Primary2" and data.get("Type") != "Episode":
userData = data.get("UserData")
if userData != None:
UnWatched = 0 if userData.get("UnplayedItemCount")==None else userData.get("UnplayedItemCount")
if UnWatched <> 0 and self.addonSettings.getSetting('showUnplayedIndicators')=='true':
query = query + "&UnplayedCount=" + str(UnWatched)
if(userData != None and userData.get("Played") == True and self.addonSettings.getSetting('showWatchedIndicators')=='true'):
query = query + "&AddPlayedIndicator=true"
PlayedPercentage = 0 if userData.get("PlayedPercentage")==None else userData.get("PlayedPercentage")
if PlayedPercentage == 0 and userData!=None and userData.get("PlayedPercentage")!=None :
PlayedPercentage = userData.get("PlayedPercentage")
if (PlayedPercentage != 100 or PlayedPercentage) != 0 and self.addonSettings.getSetting('showPlayedPrecentageIndicators')=='true':
played = str(PlayedPercentage)
height = "340"
width = "226"
elif (originalType =="Primary3" and data.get("Type") != "Episode") or originalType == "SeriesPrimary":
userData = data.get("UserData")
if userData != None:
UnWatched = 0 if userData.get("UnplayedItemCount")==None else userData.get("UnplayedItemCount")
if UnWatched <> 0 and self.addonSettings.getSetting('showUnplayedIndicators')=='true':
query = query + "&UnplayedCount=" + str(UnWatched)
if(userData != None and userData.get("Played") == True and self.addonSettings.getSetting('showWatchedIndicators')=='true'):
query = query + "&AddPlayedIndicator=true"
PlayedPercentage = 0 if userData.get("PlayedPercentage")==None else userData.get("PlayedPercentage")
if PlayedPercentage == 0 and userData!=None and userData.get("PlayedPercentage")!=None :
PlayedPercentage = userData.get("PlayedPercentage")
if (PlayedPercentage != 100 or PlayedPercentage) != 0 and self.addonSettings.getSetting('showPlayedPrecentageIndicators')=='true':
played = str(PlayedPercentage)
height = "800"
width = "550"
elif type =="Primary" and data.get("Type") == "Episode":
userData = data.get("UserData")
if userData != None:
UnWatched = 0 if userData.get("UnplayedItemCount")==None else userData.get("UnplayedItemCount")
if UnWatched <> 0 and self.addonSettings.getSetting('showUnplayedIndicators')=='true':
query = query + "&UnplayedCount=" + str(UnWatched)
if(userData != None and userData.get("Played") == True and self.addonSettings.getSetting('showWatchedIndicators')=='true'):
query = query + "&AddPlayedIndicator=true"
PlayedPercentage = 0 if userData.get("PlayedPercentage")==None else userData.get("PlayedPercentage")
if PlayedPercentage == 0 and userData!=None and userData.get("PlayedPercentage")!=None :
PlayedPercentage = userData.get("PlayedPercentage")
if (PlayedPercentage != 100 or PlayedPercentage) != 0 and self.addonSettings.getSetting('showPlayedPrecentageIndicators')=='true':
played = str(PlayedPercentage)
height = "410"
width = "770"
elif originalType =="Backdrop2" or originalType =="Thumb2" and data.get("Type") != "Episode":
userData = data.get("UserData")
if originalType =="Backdrop2":
totalbackdrops = len(data.get("BackdropImageTags"))
if totalbackdrops != 0:
index = str(randrange(0,totalbackdrops))
if userData != None:
UnWatched = 0 if userData.get("UnplayedItemCount")==None else userData.get("UnplayedItemCount")
if UnWatched <> 0 and self.addonSettings.getSetting('showUnplayedIndicators')=='true':
query = query + "&UnplayedCount=" + str(UnWatched)
if(userData != None and userData.get("Played") == True and self.addonSettings.getSetting('showWatchedIndicators')=='true'):
query = query + "&AddPlayedIndicator=true"
PlayedPercentage = 0 if userData.get("PlayedPercentage")==None else userData.get("PlayedPercentage")
if PlayedPercentage == 0 and userData!=None and userData.get("PlayedPercentage")!=None :
PlayedPercentage = userData.get("PlayedPercentage")
if (PlayedPercentage != 100 or PlayedPercentage) != 0 and self.addonSettings.getSetting('showPlayedPrecentageIndicators')=='true':
played = str(PlayedPercentage)
height = "270"
width = "480"
elif originalType =="Backdrop3" or originalType =="Thumb3" and data.get("Type") != "Episode":
userData = data.get("UserData")
if originalType =="Backdrop3":
totalbackdrops = len(data.get("BackdropImageTags"))
if totalbackdrops != 0:
index = str(randrange(0,totalbackdrops))
if userData != None:
UnWatched = 0 if userData.get("UnplayedItemCount")==None else userData.get("UnplayedItemCount")
if UnWatched <> 0 and self.addonSettings.getSetting('showUnplayedIndicators')=='true':
query = query + "&UnplayedCount=" + str(UnWatched)
if(userData != None and userData.get("Played") == True and self.addonSettings.getSetting('showWatchedIndicators')=='true'):
query = query + "&AddPlayedIndicator=true"
PlayedPercentage = 0 if userData.get("PlayedPercentage")==None else userData.get("PlayedPercentage")
if PlayedPercentage == 0 and userData!=None and userData.get("PlayedPercentage")!=None :
PlayedPercentage = userData.get("PlayedPercentage")
if (PlayedPercentage != 100 or PlayedPercentage) != 0 and self.addonSettings.getSetting('showPlayedPrecentageIndicators')=='true':
played = str(PlayedPercentage)
height = "800"
width = "1420"
# use the local image proxy server that is made available by this addons service
port = self.addonSettings.getSetting('port')
host = self.addonSettings.getSetting('ipaddress')
server = host + ":" + port
artwork = "http://" + server + "/mediabrowser/Items/" + str(id) + "/Images/" + type + "/" + index + "/" + imageTag + "/original/" + height + "/" + width + "/" + played + "?" + query
if self.addonSettings.getSetting('disableCoverArt')=='true':
artwork = artwork + "&EnableImageEnhancers=false"
self.logMsg("getArtwork : " + artwork, level=2)
# do not return non-existing images
if ( (type!="Backdrop" and imageTag=="") |
(type=="Backdrop" and data.get("BackdropImageTags") != None and len(data.get("BackdropImageTags")) == 0) |
(type=="Backdrop" and data.get("BackdropImageTag") != None and len(data.get("BackdropImageTag")) == 0)
):
if type=="Backdrop" and getSeriesData==True and data.get("ParentBackdropImageTags") == None:
artwork=''
return artwork
def getUserArtwork(self, data, type, index = "0"):
id = data.get("Id")
#query = "&type=" + type + "&tag=" + imageTag
query = ""
height = "60"
width = "60"
played = "0"
# use the local image proxy server that is made available by this addons service
port = self.addonSettings.getSetting('port')
host = self.addonSettings.getSetting('ipaddress')
server = host + ":" + port
artwork = "http://" + server + "/mediabrowser/Users/" + str(id) + "/Images/Primary/0" + "?height=60&width=60&format=png"
return artwork
def imageUrl(self, id, type, index, width, height):
port = self.addonSettings.getSetting('port')
host = self.addonSettings.getSetting('ipaddress')
server = host + ":" + port
return "http://" + server + "/mediabrowser/Items/" + str(id) + "/Images/" + type + "/" + str(index) + "/e3ab56fe27d389446754d0fb04910a34/original/" + str(height) + "/" + str(width) + "/0"
def downloadUrl(self, url, suppress=False, type="GET", popup=0 ):
self.logMsg("== ENTER: getURL ==")
try:
if url[0:4] == "http":
serversplit=2
urlsplit=3
else:
serversplit=0
urlsplit=1
server=url.split('/')[serversplit]
urlPath="/"+"/".join(url.split('/')[urlsplit:])
self.logMsg("url = " + url)
self.logMsg("server = "+str(server), level=2)
self.logMsg("urlPath = "+str(urlPath), level=2)
conn = httplib.HTTPConnection(server, timeout=20)
#head = {"Accept-Encoding" : "gzip,deflate", "Accept-Charset" : "UTF-8,*"}
if self.addonSettings.getSetting('AccessToken')==None:
self.addonSettings.setSetting('AccessToken','')
head = {"Accept-Encoding" : "gzip", "Accept-Charset" : "UTF-8,*", "X-MediaBrowser-Token" : self.addonSettings.getSetting('AccessToken')}
#head = getAuthHeader()
conn.request(method=type, url=urlPath, headers=head)
#conn.request(method=type, url=urlPath)
data = conn.getresponse()
self.logMsg("GET URL HEADERS : " + str(data.getheaders()), level=2)
link = ""
contentType = "none"
if int(data.status) == 200:
retData = data.read()
contentType = data.getheader('content-encoding')
self.logMsg("Data Len Before : " + str(len(retData)))
if(contentType == "gzip"):
retData = StringIO.StringIO(retData)
gzipper = gzip.GzipFile(fileobj=retData)
link = gzipper.read()
else:
link = retData
self.logMsg("Data Len After : " + str(len(link)))
self.logMsg("====== 200 returned =======")
self.logMsg("Content-Type : " + str(contentType))
self.logMsg(link)
self.logMsg("====== 200 finished ======")
elif ( int(data.status) == 301 ) or ( int(data.status) == 302 ):
try: conn.close()
except: pass
return data.getheader('Location')
elif int(data.status) >= 400:
error = "HTTP response error: " + str(data.status) + " " + str(data.reason)
xbmc.log (error)
if suppress is False:
if popup == 0:
xbmc.executebuiltin("XBMC.Notification(URL error: "+ str(data.reason) +",)")
else:
xbmcgui.Dialog().ok(self.getString(30135),server)
xbmc.log (error)
try: conn.close()
except: pass
return ""
else:
link = ""
except Exception, msg:
error = "Unable to connect to " + str(server) + " : " + str(msg)
xbmc.log (error)
xbmc.executebuiltin("XBMC.Notification(\"XBMB3C\": URL error: Unable to connect to server,)")
xbmcgui.Dialog().ok("",self.getString(30204))
raise
else:
try: conn.close()
except: pass
return link

View File

@ -0,0 +1,312 @@
#################################################################################################
# In Progress Updater
#################################################################################################
import xbmc
import xbmcgui
import xbmcaddon
import json
import threading
from datetime import datetime
import urllib
from DownloadUtils import DownloadUtils
_MODE_BASICPLAY=12
#define our global download utils
downloadUtils = DownloadUtils()
class InProgressUpdaterThread(threading.Thread):
logLevel = 0
def __init__(self, *args):
addonSettings = xbmcaddon.Addon(id='plugin.video.xbmb3c')
level = addonSettings.getSetting('logLevel')
self.logLevel = 0
if(level != None):
self.logLevel = int(level)
xbmc.log("XBMB3C InProgressUpdaterThread -> Log Level:" + str(self.logLevel))
threading.Thread.__init__(self, *args)
def logMsg(self, msg, level = 1):
if(self.logLevel >= level):
xbmc.log("XBMB3C InProgressUpdaterThread -> " + msg)
def run(self):
self.logMsg("Started")
self.updateInProgress()
lastRun = datetime.today()
updateInterval = 300
while (xbmc.abortRequested == False):
td = datetime.today() - lastRun
secTotal = td.seconds
if(secTotal > updateInterval):
self.updateInProgress()
lastRun = datetime.today()
xbmc.sleep(3000)
self.logMsg("Exited")
def updateInProgress(self):
self.logMsg("updateInProgress Called")
addonSettings = xbmcaddon.Addon(id='plugin.video.xbmb3c')
mb3Host = addonSettings.getSetting('ipaddress')
mb3Port = addonSettings.getSetting('port')
userName = addonSettings.getSetting('username')
userid = downloadUtils.getUserId()
self.logMsg("InProgress UserName : " + userName + " UserID : " + userid)
self.logMsg("Updating In Progress Movie List")
recentUrl = "http://" + mb3Host + ":" + mb3Port + "/mediabrowser/Users/" + userid + "/Items?Limit=30&Recursive=true&SortBy=DatePlayed&SortOrder=Descending&Fields=Path,Genres,MediaStreams,Overview,CriticRatingSummary&Filters=IsResumable&IncludeItemTypes=Movie&format=json"
jsonData = downloadUtils.downloadUrl(recentUrl, suppress=False, popup=1 )
result = json.loads(jsonData)
result = result.get("Items")
if(result == None):
result = []
WINDOW = xbmcgui.Window( 10000 )
item_count = 1
for item in result:
title = "Missing Title"
if(item.get("Name") != None):
title = item.get("Name").encode('utf-8')
rating = item.get("CommunityRating")
criticrating = item.get("CriticRating")
officialrating = item.get("OfficialRating")
criticratingsummary = ""
if(item.get("CriticRatingSummary") != None):
criticratingsummary = item.get("CriticRatingSummary").encode('utf-8')
plot = item.get("Overview")
if plot == None:
plot=''
plot=plot.encode('utf-8')
year = item.get("ProductionYear")
if(item.get("RunTimeTicks") != None):
runtime = str(int(item.get("RunTimeTicks"))/(10000000*60))
else:
runtime = "0"
userData = item.get("UserData")
if(userData != None):
reasonableTicks = int(userData.get("PlaybackPositionTicks")) / 1000
seekTime = reasonableTicks / 10000
duration = float(runtime)
resume = float(seekTime) / 60.0
if (duration == 0):
percentage=0
else:
percentage = (resume / duration) * 100.0
perasint = int(percentage)
title = str(perasint) + "% " + title
item_id = item.get("Id")
thumbnail = downloadUtils.getArtwork(item, "Primary2")
logo = downloadUtils.getArtwork(item, "Logo")
fanart = downloadUtils.getArtwork(item, "Backdrop")
medium_fanart = downloadUtils.getArtwork(item, "Backdrop3")
if item.get("ImageTags").get("Thumb") != None:
realthumbnail = downloadUtils.getArtwork(item, "Thumb3")
else:
realthumbnail = fanart
url = mb3Host + ":" + mb3Port + ',;' + item_id
playUrl = "plugin://plugin.video.xbmb3c/?url=" + url + '&mode=' + str(_MODE_BASICPLAY)
playUrl = playUrl.replace("\\\\","smb://")
playUrl = playUrl.replace("\\","/")
self.logMsg("InProgressMovieMB3." + str(item_count) + ".Title = " + title, level=2)
self.logMsg("InProgressMovieMB3." + str(item_count) + ".Thumb = " + realthumbnail, level=2)
self.logMsg("InProgressMovieMB3." + str(item_count) + ".Path = " + playUrl, level=2)
self.logMsg("InProgressMovieMB3." + str(item_count) + ".Art(fanart) = " + fanart, level=2)
self.logMsg("InProgressMovieMB3." + str(item_count) + ".Art(clearlogo) = " + logo, level=2)
self.logMsg("InProgressMovieMB3." + str(item_count) + ".Art(poster) = " + thumbnail, level=2)
self.logMsg("InProgressMovieMB3." + str(item_count) + ".Rating = " + str(rating), level=2)
self.logMsg("InProgressMovieMB3." + str(item_count) + ".CriticRating = " + str(criticrating), level=2)
self.logMsg("InProgressMovieMB3." + str(item_count) + ".CriticRatingSummary = " + criticratingsummary, level=2)
self.logMsg("InProgressMovieMB3." + str(item_count) + ".Plot = " + plot, level=2)
self.logMsg("InProgressMovieMB3." + str(item_count) + ".Year = " + str(year), level=2)
self.logMsg("InProgressMovieMB3." + str(item_count) + ".Runtime = " + str(runtime), level=2)
WINDOW.setProperty("InProgressMovieMB3." + str(item_count) + ".Title", title)
WINDOW.setProperty("InProgressMovieMB3." + str(item_count) + ".Thumb", realthumbnail)
WINDOW.setProperty("InProgressMovieMB3." + str(item_count) + ".Path", playUrl)
WINDOW.setProperty("InProgressMovieMB3." + str(item_count) + ".Art(fanart)", fanart)
WINDOW.setProperty("InProgressMovieMB3." + str(item_count) + ".Art(medium_fanart)", medium_fanart)
WINDOW.setProperty("InProgressMovieMB3." + str(item_count) + ".Art(clearlogo)", logo)
WINDOW.setProperty("InProgressMovieMB3." + str(item_count) + ".Art(poster)", thumbnail)
WINDOW.setProperty("InProgressMovieMB3." + str(item_count) + ".Rating", str(rating))
WINDOW.setProperty("InProgressMovieMB3." + str(item_count) + ".Mpaa", str(officialrating))
WINDOW.setProperty("InProgressMovieMB3." + str(item_count) + ".CriticRating", str(criticrating))
WINDOW.setProperty("InProgressMovieMB3." + str(item_count) + ".CriticRatingSummary", criticratingsummary)
WINDOW.setProperty("InProgressMovieMB3." + str(item_count) + ".Plot", plot)
WINDOW.setProperty("InProgressMovieMB3." + str(item_count) + ".Year", str(year))
WINDOW.setProperty("InProgressMovieMB3." + str(item_count) + ".Runtime", str(runtime))
WINDOW.setProperty("InProgressMovieMB3.Enabled", "true")
item_count = item_count + 1
# blank any not available
for x in range(item_count, 11):
WINDOW.setProperty("InProgressMovieMB3." + str(x) + ".Title", "")
WINDOW.setProperty("InProgressMovieMB3." + str(x) + ".Thumb", "")
WINDOW.setProperty("InProgressMovieMB3." + str(x) + ".Path", "")
WINDOW.setProperty("InProgressMovieMB3." + str(x) + ".Art(fanart)", "")
WINDOW.setProperty("InProgressMovieMB3." + str(x) + ".Art(clearlogo)", "")
WINDOW.setProperty("InProgressMovieMB3." + str(x) + ".Art(poster)", "")
WINDOW.setProperty("InProgressMovieMB3." + str(x) + ".Rating", "")
WINDOW.setProperty("InProgressMovieMB3." + str(x) + ".CriticRating", "")
WINDOW.setProperty("InProgressMovieMB3." + str(x) + ".CriticRatingSummary", "")
WINDOW.setProperty("InProgressMovieMB3." + str(x) + ".Plot", "")
WINDOW.setProperty("InProgressMovieMB3." + str(x) + ".Year", "")
WINDOW.setProperty("InProgressMovieMB3." + str(x) + ".Runtime", "")
#Updating Recent TV Show List
self.logMsg("Updating In Progress Episode List")
recentUrl = "http://" + mb3Host + ":" + mb3Port + "/mediabrowser/Users/" + userid + "/Items?Limit=30&Recursive=true&SortBy=DatePlayed&SortOrder=Descending&Fields=Path,Genres,MediaStreams,Overview,CriticRatingSummary&Filters=IsResumable&IncludeItemTypes=Episode&format=json"
jsonData = downloadUtils.downloadUrl(recentUrl, suppress=False, popup=1 )
result = json.loads(jsonData)
result = result.get("Items")
if(result == None):
result = []
item_count = 1
for item in result:
title = "Missing Title"
if(item.get("Name") != None):
title = item.get("Name").encode('utf-8')
seriesName = "Missing Name"
if(item.get("SeriesName") != None):
seriesName = item.get("SeriesName").encode('utf-8')
eppNumber = "X"
tempEpisodeNumber = "00"
if(item.get("IndexNumber") != None):
eppNumber = item.get("IndexNumber")
if eppNumber < 10:
tempEpisodeNumber = "0" + str(eppNumber)
else:
tempEpisodeNumber = str(eppNumber)
seasonNumber = item.get("ParentIndexNumber")
if seasonNumber < 10:
tempSeasonNumber = "0" + str(seasonNumber)
else:
tempSeasonNumber = str(seasonNumber)
rating = str(item.get("CommunityRating"))
plot = item.get("Overview")
if plot == None:
plot=''
plot=plot.encode('utf-8')
if(item.get("RunTimeTicks") != None):
runtime = str(int(item.get("RunTimeTicks"))/(10000000*60))
else:
runtime = "0"
userData = item.get("UserData")
if(userData != None):
reasonableTicks = int(userData.get("PlaybackPositionTicks")) / 1000
seekTime = reasonableTicks / 10000
duration = float(runtime)
resume = float(seekTime) / 60.0
if (duration == 0):
percentage=0
else:
percentage = (resume / duration) * 100.0
perasint = int(percentage)
title = str(perasint) + "% " + title
item_id = item.get("Id")
if item.get("Type") == "Episode" or item.get("Type") == "Season":
series_id = item.get("SeriesId")
poster = downloadUtils.getArtwork(item, "SeriesPrimary")
thumbnail = downloadUtils.getArtwork(item, "Primary")
logo = downloadUtils.getArtwork(item, "Logo")
fanart = downloadUtils.getArtwork(item, "Backdrop")
medium_fanart = downloadUtils.getArtwork(item, "Backdrop3")
banner = downloadUtils.getArtwork(item, "Banner")
if item.get("SeriesThumbImageTag") != None:
seriesthumbnail = downloadUtils.getArtwork(item, "Thumb3")
else:
seriesthumbnail = fanart
url = mb3Host + ":" + mb3Port + ',;' + item_id
playUrl = "plugin://plugin.video.xbmb3c/?url=" + url + '&mode=' + str(_MODE_BASICPLAY)
playUrl = playUrl.replace("\\\\","smb://")
playUrl = playUrl.replace("\\","/")
self.logMsg("InProgresstEpisodeMB3." + str(item_count) + ".EpisodeTitle = " + title, level=2)
self.logMsg("InProgresstEpisodeMB3." + str(item_count) + ".ShowTitle = " + seriesName, level=2)
self.logMsg("InProgresstEpisodeMB3." + str(item_count) + ".EpisodeNo = " + tempEpisodeNumber, level=2)
self.logMsg("InProgresstEpisodeMB3." + str(item_count) + ".SeasonNo = " + tempSeasonNumber, level=2)
self.logMsg("InProgresstEpisodeMB3." + str(item_count) + ".Thumb = " + thumbnail, level=2)
self.logMsg("InProgresstEpisodeMB3." + str(item_count) + ".Path = " + playUrl, level=2)
self.logMsg("InProgresstEpisodeMB3." + str(item_count) + ".Rating = " + rating, level=2)
self.logMsg("InProgresstEpisodeMB3." + str(item_count) + ".Art(tvshow.fanart) = " + fanart, level=2)
self.logMsg("InProgresstEpisodeMB3." + str(item_count) + ".Art(tvshow.clearlogo) = " + logo, level=2)
self.logMsg("InProgresstEpisodeMB3." + str(item_count) + ".Art(tvshow.banner) = " + banner, level=2)
self.logMsg("InProgresstEpisodeMB3." + str(item_count) + ".Art(tvshow.poster) = " + poster, level=2)
self.logMsg("InProgresstEpisodeMB3." + str(item_count) + ".Plot = " + plot, level=2)
WINDOW.setProperty("InProgresstEpisodeMB3." + str(item_count) + ".EpisodeTitle", title)
WINDOW.setProperty("InProgresstEpisodeMB3." + str(item_count) + ".ShowTitle", seriesName)
WINDOW.setProperty("InProgresstEpisodeMB3." + str(item_count) + ".EpisodeNo", tempEpisodeNumber)
WINDOW.setProperty("InProgresstEpisodeMB3." + str(item_count) + ".SeasonNo", tempSeasonNumber)
WINDOW.setProperty("InProgresstEpisodeMB3." + str(item_count) + ".Thumb", thumbnail)
WINDOW.setProperty("InProgresstEpisodeMB3." + str(item_count) + ".SeriesThumb", seriesthumbnail)
WINDOW.setProperty("InProgresstEpisodeMB3." + str(item_count) + ".Path", playUrl)
WINDOW.setProperty("InProgresstEpisodeMB3." + str(item_count) + ".Rating", rating)
WINDOW.setProperty("InProgresstEpisodeMB3." + str(item_count) + ".Art(tvshow.fanart)", fanart)
WINDOW.setProperty("InProgresstEpisodeMB3." + str(item_count) + ".Art(tvshow.medium_fanart)", medium_fanart)
WINDOW.setProperty("InProgresstEpisodeMB3." + str(item_count) + ".Art(tvshow.clearlogo)", logo)
WINDOW.setProperty("InProgresstEpisodeMB3." + str(item_count) + ".Art(tvshow.banner)", banner)
WINDOW.setProperty("InProgresstEpisodeMB3." + str(item_count) + ".Art(tvshow.poster)", poster)
WINDOW.setProperty("InProgresstEpisodeMB3." + str(item_count) + ".Plot", plot)
WINDOW.setProperty("InProgresstEpisodeMB3.Enabled", "true")
item_count = item_count + 1
# blank any not available
for x in range(item_count, 11):
WINDOW.setProperty("InProgresstEpisodeMB3." + str(x) + ".EpisodeTitle", "")
WINDOW.setProperty("InProgresstEpisodeMB3." + str(x) + ".ShowTitle", "")
WINDOW.setProperty("InProgresstEpisodeMB3." + str(x) + ".EpisodeNo", "")
WINDOW.setProperty("InProgresstEpisodeMB3." + str(x) + ".SeasonNo", "")
WINDOW.setProperty("InProgresstEpisodeMB3." + str(x) + ".Thumb", "")
WINDOW.setProperty("InProgresstEpisodeMB3." + str(x) + ".Path", "")
WINDOW.setProperty("InProgresstEpisodeMB3." + str(x) + ".Rating", "")
WINDOW.setProperty("InProgresstEpisodeMB3." + str(x) + ".Art(tvshow.fanart)", "")
WINDOW.setProperty("InProgresstEpisodeMB3." + str(x) + ".Art(tvshow.clearlogo)", "")
WINDOW.setProperty("InProgresstEpisodeMB3." + str(x) + ".Art(tvshow.banner)", "")
WINDOW.setProperty("InProgresstEpisodeMB3." + str(x) + ".Art(tvshow.poster)", "")
WINDOW.setProperty("InProgresstEpisodeMB3." + str(x) + ".Plot", "")

View File

@ -0,0 +1,250 @@
#################################################################################################
# Info Updater
#################################################################################################
import xbmc
import xbmcgui
import xbmcaddon
import json
import threading
from datetime import datetime
import urllib
from DownloadUtils import DownloadUtils
_MODE_BASICPLAY=12
#define our global download utils
downloadUtils = DownloadUtils()
class InfoUpdaterThread(threading.Thread):
logLevel = 0
def __init__(self, *args):
addonSettings = xbmcaddon.Addon(id='plugin.video.xbmb3c')
level = addonSettings.getSetting('logLevel')
self.logLevel = 0
if(level != None):
self.logLevel = int(level)
xbmc.log("XBMB3C InfoUpdaterThread -> Log Level:" + str(self.logLevel))
threading.Thread.__init__(self, *args)
def logMsg(self, msg, level = 1):
if(self.logLevel >= level):
xbmc.log("XBMB3C InfoUpdaterThread -> " + msg)
def run(self):
self.logMsg("Started")
self.updateInfo()
lastRun = datetime.today()
while (xbmc.abortRequested == False):
td = datetime.today() - lastRun
secTotal = td.seconds
if(secTotal > 300):
self.updateInfo()
lastRun = datetime.today()
xbmc.sleep(3000)
self.logMsg("Exited")
def updateInfo(self):
self.logMsg("updateInfo Called")
addonSettings = xbmcaddon.Addon(id='plugin.video.xbmb3c')
mb3Host = addonSettings.getSetting('ipaddress')
mb3Port = addonSettings.getSetting('port')
userName = addonSettings.getSetting('username')
userid = downloadUtils.getUserId()
self.logMsg("updateInfo UserID : " + userid)
self.logMsg("Updating info List")
infoUrl = "http://" + mb3Host + ":" + mb3Port + "/mediabrowser/Users/" + userid + "/Items?Fields=CollectionType&format=json"
jsonData = downloadUtils.downloadUrl(infoUrl, suppress=False, popup=1 )
result = json.loads(jsonData)
result = result.get("Items")
WINDOW = xbmcgui.Window( 10000 )
if(result == None):
result = []
item_count = 1
movie_count = 0
movie_unwatched_count = 0
tv_count = 0
episode_count = 0
episode_unwatched_count = 0
tv_unwatched_count = 0
music_count = 0
music_songs_count = 0
music_songs_unplayed_count = 0
musicvideos_count = 0
musicvideos_unwatched_count = 0
trailers_count = 0
trailers_unwatched_count = 0
photos_count = 0
channels_count = 0
for item in result:
collectionType = item.get("CollectionType")
if collectionType==None:
collectionType="unknown"
self.logMsg("collectionType " + collectionType)
userData = item.get("UserData")
if(collectionType == "movies"):
movie_count = movie_count + item.get("RecursiveItemCount")
movie_unwatched_count = movie_unwatched_count + userData.get("UnplayedItemCount")
if(collectionType == "musicvideos"):
musicvideos_count = musicvideos_count + item.get("RecursiveItemCount")
musicvideos_unwatched_count = musicvideos_unwatched_count + userData.get("UnplayedItemCount")
if(collectionType == "tvshows"):
tv_count = tv_count + item.get("ChildCount")
episode_count = episode_count + item.get("RecursiveItemCount")
episode_unwatched_count = episode_unwatched_count + userData.get("UnplayedItemCount")
if(collectionType == "music"):
music_count = music_count + item.get("ChildCount")
music_songs_count = music_songs_count + item.get("RecursiveItemCount")
music_songs_unplayed_count = music_songs_unplayed_count + userData.get("UnplayedItemCount")
if(collectionType == "photos"):
photos_count = photos_count + item.get("RecursiveItemCount")
if(item.get("Name") == "Trailers"):
trailers_count = trailers_count + item.get("RecursiveItemCount")
trailers_unwatched_count = trailers_unwatched_count + userData.get("UnplayedItemCount")
self.logMsg("MoviesCount " + str(movie_count), level=2)
self.logMsg("MoviesUnWatchedCount " + str(movie_unwatched_count), level=2)
self.logMsg("MusicVideosCount " + str(musicvideos_count), level=2)
self.logMsg("MusicVideosUnWatchedCount " + str(musicvideos_unwatched_count), level=2)
self.logMsg("TVCount " + str(tv_count), level=2)
self.logMsg("EpisodeCount " + str(episode_count), level=2)
self.logMsg("EpisodeUnWatchedCount " + str(episode_unwatched_count), level=2)
self.logMsg("MusicCount " + str(music_count), level=2)
self.logMsg("SongsCount " + str(music_songs_count), level=2)
self.logMsg("SongsUnPlayedCount " + str(music_songs_unplayed_count), level=2)
self.logMsg("TrailersCount" + str(trailers_count), level=2)
self.logMsg("TrailersUnWatchedCount" + str(trailers_unwatched_count), level=2)
self.logMsg("PhotosCount" + str(photos_count), level=2)
#item_count = item_count + 1
movie_watched_count = movie_count - movie_unwatched_count
musicvideos_watched_count = musicvideos_count - musicvideos_unwatched_count
episode_watched_count = episode_count - episode_unwatched_count
music_songs_played_count = music_songs_count - music_songs_unplayed_count
trailers_watched_count = trailers_count - trailers_unwatched_count
WINDOW.setProperty("MB3TotalMovies", str(movie_count))
WINDOW.setProperty("MB3TotalUnWatchedMovies", str(movie_unwatched_count))
WINDOW.setProperty("MB3TotalWatchedMovies", str(movie_watched_count))
WINDOW.setProperty("MB3TotalMusicVideos", str(musicvideos_count))
WINDOW.setProperty("MB3TotalUnWatchedMusicVideos", str(musicvideos_unwatched_count))
WINDOW.setProperty("MB3TotalWatchedMusicVideos", str(musicvideos_watched_count))
WINDOW.setProperty("MB3TotalTvShows", str(tv_count))
WINDOW.setProperty("MB3TotalEpisodes", str(episode_count))
WINDOW.setProperty("MB3TotalUnWatchedEpisodes", str(episode_unwatched_count))
WINDOW.setProperty("MB3TotalWatchedEpisodes", str(episode_watched_count))
WINDOW.setProperty("MB3TotalMusicAlbums", str(music_count))
WINDOW.setProperty("MB3TotalMusicSongs", str(music_songs_count))
WINDOW.setProperty("MB3TotalUnPlayedMusicSongs", str(music_songs_unplayed_count))
WINDOW.setProperty("MB3TotalPlayedMusicSongs", str(music_songs_played_count))
WINDOW.setProperty("MB3TotalTrailers", str(trailers_count))
WINDOW.setProperty("MB3TotalUnWatchedTrailers", str(trailers_unwatched_count))
WINDOW.setProperty("MB3TotalWatchedTrailers", str(trailers_watched_count))
WINDOW.setProperty("MB3TotalPhotos", str(photos_count))
userUrl = "http://" + mb3Host + ":" + mb3Port + "/mediabrowser/Users/" + userid + "?format=json"
jsonData = downloadUtils.downloadUrl(userUrl, suppress=False, popup=1 )
result = json.loads(jsonData)
userImage = downloadUtils.getUserArtwork(result, "Primary")
WINDOW.setProperty("MB3UserImage", userImage)
xbmc.log("XBMB3C MB3UserImage -> " + userImage)
self.logMsg("InfoTV start")
infoTVUrl = "http://" + mb3Host + ":" + mb3Port + "/mediabrowser/Users/" + userid + "/Items?&IncludeItemTypes=Series&Recursive=true&SeriesStatus=Continuing&format=json"
jsonData = downloadUtils.downloadUrl(infoTVUrl, suppress=False, popup=1 )
result = json.loads(jsonData)
self.logMsg("InfoTV Json Data : " + str(result), level=2)
totalRunning = result.get("TotalRecordCount")
self.logMsg("TotalRunningCount " + str(totalRunning))
WINDOW.setProperty("MB3TotalRunningTvShows", str(totalRunning))
self.logMsg("InfoNextAired start")
InfoNextAiredUrl = "http://" + mb3Host + ":" + mb3Port + "/mediabrowser/Users/" + userid + "/Items?IsUnaired=true&SortBy=PremiereDate%2CAirTime%2CSortName&SortOrder=Ascending&IncludeItemTypes=Episode&Limit=1&Recursive=true&Fields=SeriesInfo%2CUserData&format=json"
jsonData = downloadUtils.downloadUrl(InfoNextAiredUrl, suppress=False, popup=1 )
result = json.loads(jsonData)
self.logMsg("InfoNextAired Json Data : " + str(result), level=2)
result = result.get("Items")
if(result == None):
result = []
episode = ""
for item in result:
title = ""
seriesName = ""
if(item.get("SeriesName") != None):
seriesName = item.get("SeriesName").encode('utf-8')
if(item.get("Name") != None):
title = item.get("Name").encode('utf-8')
eppNumber = ""
tempEpisodeNumber = ""
if(item.get("IndexNumber") != None):
eppNumber = item.get("IndexNumber")
if eppNumber < 10:
tempEpisodeNumber = "0" + str(eppNumber)
else:
tempEpisodeNumber = str(eppNumber)
seasonNumber = item.get("ParentIndexNumber")
if seasonNumber < 10:
tempSeasonNumber = "0" + str(seasonNumber)
else:
tempSeasonNumber = str(seasonNumber)
episode = seriesName + " - " + title + " - S" + tempSeasonNumber + "E" + tempEpisodeNumber
self.logMsg("MB3NextAiredEpisode" + episode)
WINDOW.setProperty("MB3NextAiredEpisode", episode)
self.logMsg("InfoNextAired end")
today = datetime.today()
dateformat = today.strftime("%Y-%m-%d")
nextAiredUrl = "http://" + mb3Host + ":" + mb3Port + "/mediabrowser/Users/" + userid + "/Items?IsUnaired=true&SortBy=PremiereDate%2CAirTime%2CSortName&SortOrder=Ascending&IncludeItemTypes=Episode&Recursive=true&Fields=SeriesInfo%2CUserData&MinPremiereDate=" + str(dateformat) + "&MaxPremiereDate=" + str(dateformat) + "&format=json"
jsonData = downloadUtils.downloadUrl(nextAiredUrl, suppress=False, popup=1 )
result = json.loads(jsonData)
self.logMsg("InfoNextAired total url: " + nextAiredUrl)
self.logMsg("InfoNextAired total Json Data : " + str(result), level=2)
totalToday = result.get("TotalRecordCount")
self.logMsg("MB3NextAiredTotalToday " + str(totalToday))
WINDOW.setProperty("MB3NextAiredTotalToday", str(totalToday))
self.logMsg("Channels start")
channelsUrl = "http://" + mb3Host + ":" + mb3Port + "/mediabrowser/Channels/?format=json"
jsonData = downloadUtils.downloadUrl(channelsUrl, suppress=False, popup=1 )
result = json.loads(jsonData)
self.logMsg("Channels Json Data : " + str(result), level=2)
totalChannels = result.get("TotalRecordCount")
self.logMsg("TotalChannels " + str(totalRunning))
WINDOW.setProperty("MB3TotalChannels", str(totalChannels))

227
resources/lib/ItemInfo.py Normal file
View File

@ -0,0 +1,227 @@
import sys
import xbmc
import xbmcgui
import xbmcaddon
import json as json
import urllib
from DownloadUtils import DownloadUtils
_MODE_BASICPLAY=12
_MODE_CAST_LIST=14
_MODE_PERSON_DETAILS=15
class ItemInfo(xbmcgui.WindowXMLDialog):
id = ""
playUrl = ""
def __init__(self, *args, **kwargs):
xbmcgui.WindowXMLDialog.__init__(self, *args, **kwargs)
xbmc.log("WINDOW INITIALISED")
def onInit(self):
self.action_exitkeys_id = [10, 13]
__settings__ = xbmcaddon.Addon(id='plugin.video.xbmb3c')
port = __settings__.getSetting('port')
host = __settings__.getSetting('ipaddress')
server = host + ":" + port
downloadUtils = DownloadUtils()
userid = downloadUtils.getUserId()
jsonData = downloadUtils.downloadUrl("http://" + server + "/mediabrowser/Users/" + userid + "/Items/" + self.id + "?format=json", suppress=False, popup=1 )
item = json.loads(jsonData)
id = item.get("Id")
name = item.get("Name")
image = downloadUtils.getArtwork(item, "Primary")
fanArt = downloadUtils.getArtwork(item, "Backdrop")
# calculate the percentage complete
userData = item.get("UserData")
cappedPercentage = None
if(userData != None):
playBackTicks = float(userData.get("PlaybackPositionTicks"))
if(playBackTicks != None and playBackTicks > 0):
runTimeTicks = float(item.get("RunTimeTicks", "0"))
if(runTimeTicks > 0):
percentage = int((playBackTicks / runTimeTicks) * 100.0)
cappedPercentage = percentage - (percentage % 10)
if(cappedPercentage == 0):
cappedPercentage = 10
if(cappedPercentage == 100):
cappedPercentage = 90
episodeInfo = ""
type = item.get("Type")
if(type == "Episode" or type == "Season"):
name = item.get("SeriesName") + ": " + name
season = str(item.get("ParentIndexNumber")).zfill(2)
episodeNum = str(item.get("IndexNumber")).zfill(2)
episodeInfo = "S" + season + "xE" + episodeNum
url = server + ',;' + id
url = urllib.quote(url)
self.playUrl = "plugin://plugin.video.xbmb3c/?url=" + url + '&mode=' + str(_MODE_BASICPLAY)
self.peopleUrl = "XBMC.Container.Update(plugin://plugin.video.xbmb3c?mode=" + str(_MODE_CAST_LIST) + "&id=" + id + ")"
#self.peopleUrl = "XBMC.RunPlugin(plugin://plugin.video.xbmb3c?mode=" + str(_MODE_CAST_LIST) + "&id=" + id + ")"
# all all the media stream info
mediaList = self.getControl(3220)
mediaStreams = item.get("MediaStreams")
if(mediaStreams != None):
for mediaStream in mediaStreams:
if(mediaStream.get("Type") == "Video"):
videocodec = mediaStream.get("Codec")
if(videocodec == "mpeg2video"):
videocodec = "mpeg2"
height = str(mediaStream.get("Height"))
width = str(mediaStream.get("Width"))
aspectratio = mediaStream.get("AspectRatio")
fr = mediaStream.get("RealFrameRate")
videoInfo = width + "x" + height + " " + videocodec + " " + str(round(fr, 2))
listItem = xbmcgui.ListItem("Video:", videoInfo)
mediaList.addItem(listItem)
if(mediaStream.get("Type") == "Audio"):
audiocodec = mediaStream.get("Codec")
channels = mediaStream.get("Channels")
lang = mediaStream.get("Language")
audioInfo = audiocodec + " " + str(channels)
if(lang != None and len(lang) > 0 and lang != "und"):
audioInfo = audioInfo + " " + lang
listItem = xbmcgui.ListItem("Audio:", audioInfo)
mediaList.addItem(listItem)
if(mediaStream.get("Type") == "Subtitle"):
lang = mediaStream.get("Language")
codec = mediaStream.get("Codec")
subInfo = codec
if(lang != None and len(lang) > 0 and lang != "und"):
subInfo = subInfo + " " + lang
listItem = xbmcgui.ListItem("Sub:", subInfo)
mediaList.addItem(listItem)
#for x in range(0, 10):
# listItem = xbmcgui.ListItem("Test:", "Test 02 " + str(x))
# mediaList.addItem(listItem)
# add overview
overview = item.get("Overview")
self.getControl(3223).setText(overview)
# add people
peopleList = self.getControl(3230)
people = item.get("People")
for person in people:
displayName = person.get("Name")
role = person.get("Role")
id = person.get("Id")
tag = person.get("PrimaryImageTag")
baseName = person.get("Name")
baseName = baseName.replace(" ", "+")
baseName = baseName.replace("&", "_")
baseName = baseName.replace("?", "_")
baseName = baseName.replace("=", "_")
actionUrl = "plugin://plugin.video.xbmb3c?mode=" + str(_MODE_PERSON_DETAILS) +"&name=" + baseName
if(tag != None and len(tag) > 0):
thumbPath = downloadUtils.imageUrl(id, "Primary", 0, 400, 400)
listItem = xbmcgui.ListItem(label=displayName, label2=role, iconImage=thumbPath, thumbnailImage=thumbPath)
else:
listItem = xbmcgui.ListItem(label=displayName, label2=role)
listItem.setProperty("ActionUrl", actionUrl)
peopleList.addItem(listItem)
# add general info
infoList = self.getControl(3226)
listItem = xbmcgui.ListItem("Year:", str(item.get("ProductionYear")))
infoList.addItem(listItem)
listItem = xbmcgui.ListItem("Rating:", str(item.get("CommunityRating")))
infoList.addItem(listItem)
listItem = xbmcgui.ListItem("MPAA:", str(item.get("OfficialRating")))
infoList.addItem(listItem)
duration = str(int(item.get("RunTimeTicks", "0"))/(10000000*60))
listItem = xbmcgui.ListItem("RunTime:", str(duration) + " Minutes")
infoList.addItem(listItem)
genre = ""
genres = item.get("Genres")
if(genres != None):
for genre_string in genres:
if genre == "": #Just take the first genre
genre = genre_string
else:
genre = genre + " / " + genre_string
listItem = xbmcgui.ListItem("Genre:", genre)
infoList.addItem(listItem)
path = item.get('Path')
listItem = xbmcgui.ListItem("Path:", path)
infoList.addItem(listItem)
# add resume percentage text to name
addResumePercent = __settings__.getSetting('addResumePercent') == 'true'
if (addResumePercent and cappedPercentage != None):
name = name + " (" + str(cappedPercentage) + "%)"
self.getControl(3000).setLabel(name)
self.getControl(3003).setLabel(episodeInfo)
self.getControl(3001).setImage(fanArt)
if(type == "Episode"):
self.getControl(3009).setImage(image)
if(cappedPercentage != None):
self.getControl(3010).setImage("Progress\progress_" + str(cappedPercentage) + ".png")
else:
self.getControl(3011).setImage(image)
if(cappedPercentage != None):
self.getControl(3012).setImage("Progress\progress_" + str(cappedPercentage) + ".png")
# disable play button
if(type == "Season" or type == "Series"):
self.setFocusId(3226)
self.getControl(3002).setEnabled(False)
def setId(self, id):
self.id = id
def onFocus(self, controlId):
pass
def doAction(self):
pass
def closeDialog(self):
self.close()
def onClick(self, controlID):
if(controlID == 3002):
# close all dialogs when playing an item
xbmc.executebuiltin("Dialog.Close(all,true)")
xbmc.executebuiltin("RunPlugin(" + self.playUrl + ")")
self.close()
elif(controlID == 3230):
peopleList = self.getControl(3230)
item = peopleList.getSelectedItem()
action = item.getProperty("ActionUrl")
xbmc.log(action)
xbmc.executebuiltin("RunPlugin(" + action + ")")
pass

116
resources/lib/MenuLoad.py Normal file
View File

@ -0,0 +1,116 @@
#################################################################################################
# menu item loader thread
# this loads the favourites.xml and sets the windows props for the menus to auto display in skins
#################################################################################################
import xbmc
import xbmcgui
import xbmcaddon
import xml.etree.ElementTree as xml
import os
import threading
class LoadMenuOptionsThread(threading.Thread):
logLevel = 0
addonSettings = None
getString = None
def __init__(self, *args):
addonSettings = xbmcaddon.Addon(id='plugin.video.xbmb3c')
self.addonSettings = xbmcaddon.Addon(id='plugin.video.xbmb3c')
level = addonSettings.getSetting('logLevel')
self.logLevel = 0
self.getString = self.addonSettings.getLocalizedString
if(level != None):
self.logLevel = int(level)
xbmc.log("XBMB3C LoadMenuOptionsThread -> Log Level:" + str(self.logLevel))
threading.Thread.__init__(self, *args)
def logMsg(self, msg, level = 1):
if(self.logLevel >= level):
xbmc.log("XBMB3C LoadMenuOptionsThread -> " + msg)
def run(self):
try:
self.run_internal()
except Exception, e:
xbmcgui.Dialog().ok(self.getString(30205), str(e))
raise
def run_internal(self):
self.logMsg("LoadMenuOptionsThread Started")
lastFavPath = ""
favourites_file = os.path.join(xbmc.translatePath('special://profile'), "favourites.xml")
self.loadMenuOptions(favourites_file)
lastFavPath = favourites_file
try:
lastModLast = os.stat(favourites_file).st_mtime
except:
lastModLast = 0;
while (xbmc.abortRequested == False):
favourites_file = os.path.join(xbmc.translatePath('special://profile'), "favourites.xml")
try:
lastMod = os.stat(favourites_file).st_mtime
except:
lastMod = 0;
if(lastFavPath != favourites_file or lastModLast != lastMod):
self.loadMenuOptions(favourites_file)
lastFavPath = favourites_file
lastModLast = lastMod
xbmc.sleep(3000)
self.logMsg("LoadMenuOptionsThread Exited")
def loadMenuOptions(self, pathTofavourites):
self.logMsg("LoadMenuOptionsThread -> Loading menu items from : " + pathTofavourites)
WINDOW = xbmcgui.Window( 10000 )
menuItem = 0
try:
tree = xml.parse(pathTofavourites)
rootElement = tree.getroot()
except Exception, e:
self.logMsg("LoadMenuOptionsThread -> Error Parsing favourites.xml : " + str(e), level=0)
for x in range(0, 10):
WINDOW.setProperty("xbmb3c_menuitem_name_" + str(x), "")
WINDOW.setProperty("xbmb3c_menuitem_action_" + str(x), "")
WINDOW.setProperty("xbmb3c_menuitem_collection_" + str(x), "")
return
for child in rootElement.findall('favourite'):
name = child.get('name')
action = child.text
if(len(name) > 1 and name[0:1] != '-'):
WINDOW.setProperty("xbmb3c_menuitem_name_" + str(menuItem), name)
WINDOW.setProperty("xbmb3c_menuitem_action_" + str(menuItem), action)
WINDOW.setProperty("xbmb3c_menuitem_collection_" + str(menuItem), name)
self.logMsg("xbmb3c_menuitem_name_" + str(menuItem) + " : " + name)
self.logMsg("xbmb3c_menuitem_action_" + str(menuItem) + " : " + action)
self.logMsg("xbmb3c_menuitem_collection_" + str(menuItem) + " : " + name)
menuItem = menuItem + 1
for x in range(menuItem, menuItem+10):
WINDOW.setProperty("xbmb3c_menuitem_name_" + str(x), "")
WINDOW.setProperty("xbmb3c_menuitem_action_" + str(x), "")
self.logMsg("xbmb3c_menuitem_name_" + str(x) + " : ")
self.logMsg("xbmb3c_menuitem_action_" + str(x) + " : ")
self.logMsg("xbmb3c_menuitem_collection_" + str(x) + " : ")

View File

@ -0,0 +1,196 @@
#################################################################################################
# NextUp TV Updater
#################################################################################################
import xbmc
import xbmcgui
import xbmcaddon
import json
import threading
from datetime import datetime
import urllib
from DownloadUtils import DownloadUtils
_MODE_BASICPLAY=12
#define our global download utils
downloadUtils = DownloadUtils()
class NextUpUpdaterThread(threading.Thread):
logLevel = 0
def __init__(self, *args):
addonSettings = xbmcaddon.Addon(id='plugin.video.xbmb3c')
level = addonSettings.getSetting('logLevel')
self.logLevel = 0
if(level != None):
self.logLevel = int(level)
xbmc.log("XBMB3C NextUpUpdaterThread -> Log Level:" + str(self.logLevel))
threading.Thread.__init__(self, *args)
def logMsg(self, msg, level = 1):
if(self.logLevel >= level):
xbmc.log("XBMB3C NextUpUpdaterThread -> " + msg)
def run(self):
self.logMsg("Started")
self.updateNextUp()
lastRun = datetime.today()
while (xbmc.abortRequested == False):
td = datetime.today() - lastRun
secTotal = td.seconds
if(secTotal > 300):
self.updateNextUp()
lastRun = datetime.today()
xbmc.sleep(3000)
self.logMsg("Exited")
def updateNextUp(self):
self.logMsg("updateNextUp Called")
addonSettings = xbmcaddon.Addon(id='plugin.video.xbmb3c')
mb3Host = addonSettings.getSetting('ipaddress')
mb3Port = addonSettings.getSetting('port')
userName = addonSettings.getSetting('username')
userid = downloadUtils.getUserId()
self.logMsg("updateNextUp UserID : " + userid)
self.logMsg("Updating NextUp List")
nextUpUrl = "http://" + mb3Host + ":" + mb3Port + "/mediabrowser/Shows/NextUp?UserId=" + userid + "&Fields=Path,Genres,MediaStreams,Overview&format=json"
jsonData = downloadUtils.downloadUrl(nextUpUrl, suppress=False, popup=1 )
result = json.loads(jsonData)
self.logMsg("NextUP TV Show Json Data : " + str(result), level=2)
result = result.get("Items")
WINDOW = xbmcgui.Window( 10000 )
if(result == None):
result = []
item_count = 1
for item in result:
title = "Missing Title"
if(item.get("Name") != None):
title = item.get("Name").encode('utf-8')
seriesName = "Missing Name"
if(item.get("SeriesName") != None):
seriesName = item.get("SeriesName").encode('utf-8')
eppNumber = "X"
tempEpisodeNumber = "XX"
if(item.get("IndexNumber") != None):
eppNumber = item.get("IndexNumber")
if eppNumber < 10:
tempEpisodeNumber = "0" + str(eppNumber)
else:
tempEpisodeNumber = str(eppNumber)
seasonNumber = item.get("ParentIndexNumber")
tempSeasonNumber = "XX"
if seasonNumber < 10:
tempSeasonNumber = "0" + str(seasonNumber)
else:
tempSeasonNumber = str(seasonNumber)
rating = str(item.get("CommunityRating"))
plot = item.get("Overview")
if plot == None:
plot=''
plot=plot.encode('utf-8')
item_id = item.get("Id")
poster = downloadUtils.getArtwork(item, "SeriesPrimary")
small_poster = downloadUtils.getArtwork(item, "Primary2")
thumbnail = downloadUtils.getArtwork(item, "Primary")
logo = downloadUtils.getArtwork(item, "Logo")
fanart = downloadUtils.getArtwork(item, "Backdrop")
medium_fanart = downloadUtils.getArtwork(item, "Backdrop3")
banner = downloadUtils.getArtwork(item, "Banner")
if item.get("SeriesThumbImageTag") != None:
seriesthumbnail = downloadUtils.getArtwork(item, "Thumb3")
else:
seriesthumbnail = fanart
url = mb3Host + ":" + mb3Port + ',;' + item_id
playUrl = "plugin://plugin.video.xbmb3c/?url=" + url + '&mode=' + str(_MODE_BASICPLAY)
playUrl = playUrl.replace("\\\\","smb://")
playUrl = playUrl.replace("\\","/")
# Process UserData
userData = item.get("UserData")
if(userData != None):
resume = str(userData.get("PlaybackPositionTicks"))
if (resume == "0"):
resume = "False"
else:
resume = "True"
self.logMsg("NextUpEpisodeMB3." + str(item_count) + ".EpisodeTitle = " + title, level=2)
self.logMsg("NextUpEpisodeMB3." + str(item_count) + ".ShowTitle = " + seriesName, level=2)
self.logMsg("NextUpEpisodeMB3." + str(item_count) + ".EpisodeNo = " + tempEpisodeNumber, level=2)
self.logMsg("NextUpEpisodeMB3." + str(item_count) + ".SeasonNo = " + tempSeasonNumber, level=2)
self.logMsg("NextUpEpisodeMB3." + str(item_count) + ".Thumb = " + thumbnail, level=2)
self.logMsg("NextUpEpisodeMB3." + str(item_count) + ".Path = " + playUrl, level=2)
self.logMsg("NextUpEpisodeMB3." + str(item_count) + ".Rating = " + rating, level=2)
self.logMsg("NextUpEpisodeMB3." + str(item_count) + ".Art(tvshow.fanart) = " + fanart, level=2)
self.logMsg("NextUpEpisodeMB3." + str(item_count) + ".Art(tvshow.clearlogo) = " + logo, level=2)
self.logMsg("NextUpEpisodeMB3." + str(item_count) + ".Art(tvshow.banner) = " + banner, level=2)
self.logMsg("NextUpEpisodeMB3." + str(item_count) + ".Art(tvshow.poster) = " + poster, level=2)
self.logMsg("NextUpEpisodeMB3." + str(item_count) + ".Plot = " + plot, level=2)
self.logMsg("NextUpEpisodeMB3." + str(item_count) + ".Resume = " + resume, level=2)
WINDOW.setProperty("NextUpEpisodeMB3." + str(item_count) + ".EpisodeTitle", title)
WINDOW.setProperty("NextUpEpisodeMB3." + str(item_count) + ".ShowTitle", seriesName)
WINDOW.setProperty("NextUpEpisodeMB3." + str(item_count) + ".EpisodeNo", tempEpisodeNumber)
WINDOW.setProperty("NextUpEpisodeMB3." + str(item_count) + ".SeasonNo", tempSeasonNumber)
WINDOW.setProperty("NextUpEpisodeMB3." + str(item_count) + ".Thumb", thumbnail)
WINDOW.setProperty("NextUpEpisodeMB3." + str(item_count) + ".SeriesThumb", seriesthumbnail)
WINDOW.setProperty("NextUpEpisodeMB3." + str(item_count) + ".Path", playUrl)
WINDOW.setProperty("NextUpEpisodeMB3." + str(item_count) + ".Rating", rating)
WINDOW.setProperty("NextUpEpisodeMB3." + str(item_count) + ".Art(tvshow.fanart)", fanart)
WINDOW.setProperty("NextUpEpisodeMB3." + str(item_count) + ".Art(tvshow.medium_fanart)", medium_fanart)
WINDOW.setProperty("NextUpEpisodeMB3." + str(item_count) + ".Art(tvshow.clearlogo)", logo)
WINDOW.setProperty("NextUpEpisodeMB3." + str(item_count) + ".Art(tvshow.banner)", banner)
WINDOW.setProperty("NextUpEpisodeMB3." + str(item_count) + ".Art(tvshow.poster)", poster)
WINDOW.setProperty("NextUpEpisodeMB3." + str(item_count) + ".Art(tvshow.small_poster)", small_poster)
WINDOW.setProperty("NextUpEpisodeMB3." + str(item_count) + ".Plot", plot)
WINDOW.setProperty("NextUpEpisodeMB3." + str(item_count) + ".Resume", resume)
WINDOW.setProperty("NextUpEpisodeMB3.Enabled", "true")
item_count = item_count + 1
if(item_count < 10):
# blank any not available
for x in range(item_count, 11):
WINDOW.setProperty("NextUpEpisodeMB3." + str(x) + ".EpisodeTitle", "")
WINDOW.setProperty("NextUpEpisodeMB3." + str(x) + ".ShowTitle", "")
WINDOW.setProperty("NextUpEpisodeMB3." + str(x) + ".EpisodeNo", "")
WINDOW.setProperty("NextUpEpisodeMB3." + str(x) + ".SeasonNo", "")
WINDOW.setProperty("NextUpEpisodeMB3." + str(x) + ".Thumb", "")
WINDOW.setProperty("NextUpEpisodeMB3." + str(x) + ".Path", "")
WINDOW.setProperty("NextUpEpisodeMB3." + str(x) + ".Rating", "")
WINDOW.setProperty("NextUpEpisodeMB3." + str(x) + ".Art(tvshow.fanart)", "")
WINDOW.setProperty("NextUpEpisodeMB3." + str(x) + ".Art(tvshow.clearlogo)", "")
WINDOW.setProperty("NextUpEpisodeMB3." + str(x) + ".Art(tvshow.banner)", "")
WINDOW.setProperty("NextUpEpisodeMB3." + str(x) + ".Art(tvshow.poster)", "")
WINDOW.setProperty("NextUpEpisodeMB3." + str(x) + ".Plot", "")
WINDOW.setProperty("NextUpEpisodeMB3." + str(x) + ".Resume", "")

175
resources/lib/PersonInfo.py Normal file
View File

@ -0,0 +1,175 @@
import sys
import xbmc
import xbmcgui
import xbmcaddon
import json as json
import urllib
from DownloadUtils import DownloadUtils
_MODE_GETCONTENT=0
_MODE_ITEM_DETAILS=17
class PersonInfo(xbmcgui.WindowXMLDialog):
pluginCastLink = ""
showMovies = False
personName = ""
def __init__(self, *args, **kwargs):
xbmcgui.WindowXMLDialog.__init__(self, *args, **kwargs)
def onInit(self):
self.action_exitkeys_id = [10, 13]
__settings__ = xbmcaddon.Addon(id='plugin.video.xbmb3c')
port = __settings__.getSetting('port')
host = __settings__.getSetting('ipaddress')
server = host + ":" + port
downloadUtils = DownloadUtils()
userid = downloadUtils.getUserId()
jsonData = downloadUtils.downloadUrl("http://" + server + "/mediabrowser/Persons/" + self.personName + "?format=json", suppress=False, popup=1 )
result = json.loads(jsonData)
name = result.get("Name")
id = result.get("Id")
# other lib items count
contentCounts = ""
if(result.get("AdultVideoCount") != None and result.get("AdultVideoCount") > 0):
contentCounts = contentCounts + "\nAdult Count : " + str(result.get("AdultVideoCount"))
if(result.get("MovieCount") != None and result.get("MovieCount") > 0):
contentCounts = contentCounts + "\nMovie Count : " + str(result.get("MovieCount"))
if(result.get("SeriesCount") != None and result.get("SeriesCount") > 0):
contentCounts = contentCounts + "\nSeries Count : " + str(result.get("SeriesCount"))
if(result.get("EpisodeCount") != None and result.get("EpisodeCount") > 0):
contentCounts = contentCounts + "\nEpisode Count : " + str(result.get("EpisodeCount"))
if(len(contentCounts) > 0):
contentCounts = "Total Library Counts:" + contentCounts
#overview
overview = ""
if(len(contentCounts) > 0):
overview = contentCounts + "\n\n"
over = result.get("Overview")
if(over == None or over == ""):
overview = overview + "No details available"
else:
overview = overview + over
#person image
image = downloadUtils.getArtwork(result, "Primary")
#get other movies
encoded = name.encode("utf-8")
encoded = urllib.quote(encoded)
url = "http://" + server + "/mediabrowser/Users/" + userid + "/Items/?Recursive=True&Person=" + encoded + "&format=json"
xbmc.log("URL: " + url)
jsonData = downloadUtils.downloadUrl(url, suppress=False, popup=1 )
otherMovieResult = json.loads(jsonData)
baseName = name.replace(" ", "+")
baseName = baseName.replace("&", "_")
baseName = baseName.replace("?", "_")
baseName = baseName.replace("=", "_")
#detailsString = getDetailsString()
#search_url = "http://" + host + ":" + port + "/mediabrowser/Users/" + userid + "/Items/?Recursive=True&Person=PERSON_NAME&Fields=" + detailsString + "&format=json"
search_url = "http://" + host + ":" + port + "/mediabrowser/Users/" + userid + "/Items/?Recursive=True&Person=PERSON_NAME&format=json"
search_url = urllib.quote(search_url)
search_url = search_url.replace("PERSON_NAME", baseName)
self.pluginCastLink = "XBMC.Container.Update(plugin://plugin.video.xbmb3c?mode=" + str(_MODE_GETCONTENT) + "&url=" + search_url + ")"
otherItemsList = None
try:
otherItemsList = self.getControl(3010)
items = otherMovieResult.get("Items")
if(items == None):
items = []
for item in items:
item_id = item.get("Id")
item_name = item.get("Name")
type_info = ""
image_id = item_id
item_type = item.get("Type")
if(item_type == "Season"):
image_id = item.get("SeriesId")
season = item.get("IndexNumber")
type_info = "Season " + str(season).zfill(2)
elif(item_type == "Series"):
image_id = item.get("Id")
type_info = "Series"
elif(item_type == "Movie"):
image_id = item.get("Id")
type_info = "Movie"
elif(item_type == "Episode"):
image_id = item.get("SeriesId")
season = item.get("ParentIndexNumber")
eppNum = item.get("IndexNumber")
type_info = "S" + str(season).zfill(2) + "E" + str(eppNum).zfill(2)
thumbPath = downloadUtils.imageUrl(image_id, "Primary", 0, 200, 200)
listItem = xbmcgui.ListItem(label=item_name, label2=type_info, iconImage=thumbPath, thumbnailImage=thumbPath)
actionUrl = "plugin://plugin.video.xbmb3c?id=" + item_id + "&mode=" + str(_MODE_ITEM_DETAILS)
listItem.setProperty("ActionUrl", actionUrl)
otherItemsList.addItem(listItem)
except Exception, e:
xbmc.log("Exception : " + str(e))
pass
# set the dialog data
self.getControl(3000).setLabel(name)
self.getControl(3001).setText(overview)
self.getControl(3009).setImage(image)
def setPersonName(self, name):
self.personName = name
def setInfo(self, data):
self.details = data
def onFocus(self, controlId):
pass
def doAction(self):
pass
def closeDialog(self):
self.close()
def onClick(self, controlID):
if(controlID == 3002):
self.showMovies = True
xbmc.executebuiltin('Dialog.Close(movieinformation)')
self.close()
elif(controlID == 3010):
#xbmc.executebuiltin("Dialog.Close(all,true)")
itemList = self.getControl(3010)
item = itemList.getSelectedItem()
action = item.getProperty("ActionUrl")
xbmc.executebuiltin("RunPlugin(" + action + ")")
self.close()
pass

View File

@ -0,0 +1,319 @@
#################################################################################################
# Random Info Updater
#################################################################################################
import xbmc
import xbmcgui
import xbmcaddon
import json
import threading
from datetime import datetime
import urllib
from DownloadUtils import DownloadUtils
_MODE_BASICPLAY=12
#define our global download utils
downloadUtils = DownloadUtils()
class RandomInfoUpdaterThread(threading.Thread):
logLevel = 0
def __init__(self, *args):
addonSettings = xbmcaddon.Addon(id='plugin.video.xbmb3c')
level = addonSettings.getSetting('logLevel')
self.logLevel = 0
if(level != None):
self.logLevel = int(level)
xbmc.log("XBMB3C RandomInfoUpdaterThread -> Log Level:" + str(self.logLevel))
threading.Thread.__init__(self, *args)
def logMsg(self, msg, level = 1):
if(self.logLevel >= level):
xbmc.log("XBMB3C RandomInfoUpdaterThread -> " + msg)
def run(self):
self.logMsg("Started")
self.updateRandom()
lastRun = datetime.today()
while (xbmc.abortRequested == False):
td = datetime.today() - lastRun
secTotal = td.seconds
if(secTotal > 300):
self.updateRandom()
lastRun = datetime.today()
xbmc.sleep(3000)
self.logMsg("Exited")
def updateRandom(self):
self.logMsg("updateRandomMovies Called")
addonSettings = xbmcaddon.Addon(id='plugin.video.xbmb3c')
mb3Host = addonSettings.getSetting('ipaddress')
mb3Port = addonSettings.getSetting('port')
userName = addonSettings.getSetting('username')
userid = downloadUtils.getUserId()
self.logMsg("updateRandomMovies UserID : " + userid)
self.logMsg("Updating Random Movie List")
randomUrl = "http://" + mb3Host + ":" + mb3Port + "/mediabrowser/Users/" + userid + "/Items?Limit=30&Recursive=true&SortBy=Random&Fields=Path,Genres,MediaStreams,Overview,CriticRatingSummary&SortOrder=Descending&Filters=IsUnplayed,IsNotFolder&IncludeItemTypes=Movie&format=json"
jsonData = downloadUtils.downloadUrl(randomUrl, suppress=False, popup=1 )
result = json.loads(jsonData)
self.logMsg("Random Movie Json Data : " + str(result), level=2)
result = result.get("Items")
if(result == None):
result = []
WINDOW = xbmcgui.Window( 10000 )
item_count = 1
for item in result:
title = "Missing Title"
if(item.get("Name") != None):
title = item.get("Name").encode('utf-8')
rating = item.get("CommunityRating")
criticrating = item.get("CriticRating")
officialrating = item.get("OfficialRating")
criticratingsummary = ""
if(item.get("CriticRatingSummary") != None):
criticratingsummary = item.get("CriticRatingSummary").encode('utf-8')
plot = item.get("Overview")
if plot == None:
plot=''
plot=plot.encode('utf-8')
year = item.get("ProductionYear")
if(item.get("RunTimeTicks") != None):
runtime = str(int(item.get("RunTimeTicks"))/(10000000*60))
else:
runtime = "0"
item_id = item.get("Id")
thumbnail = downloadUtils.getArtwork(item, "Primary")
logo = downloadUtils.getArtwork(item, "Logo")
fanart = downloadUtils.getArtwork(item, "Backdrop")
medium_fanart = downloadUtils.getArtwork(item, "Backdrop3")
if (item.get("ImageTags") != None and item.get("ImageTags").get("Thumb") != None):
realthumb = downloadUtils.getArtwork(item, "Thumb3")
else:
realthumb = fanart
url = mb3Host + ":" + mb3Port + ',;' + item_id
playUrl = "plugin://plugin.video.xbmb3c/?url=" + url + '&mode=' + str(_MODE_BASICPLAY)
playUrl = playUrl.replace("\\\\","smb://")
playUrl = playUrl.replace("\\","/")
self.logMsg("RandomMovieMB3." + str(item_count) + ".Title = " + title, level=2)
self.logMsg("RandomMovieMB3." + str(item_count) + ".Thumb = " + thumbnail, level=2)
self.logMsg("RandomMovieMB3." + str(item_count) + ".Path = " + playUrl, level=2)
self.logMsg("RandomMovieMB3." + str(item_count) + ".Art(fanart) = " + fanart, level=2)
self.logMsg("RandomMovieMB3." + str(item_count) + ".Art(clearlogo) = " + logo, level=2)
self.logMsg("RandomMovieMB3." + str(item_count) + ".Art(poster) = " + thumbnail, level=2)
self.logMsg("RandomMovieMB3." + str(item_count) + ".Rating = " + str(rating), level=2)
self.logMsg("RandomMovieMB3." + str(item_count) + ".CriticRating = " + str(criticrating), level=2)
self.logMsg("RandomMovieMB3." + str(item_count) + ".CriticRatingSummary = " + criticratingsummary, level=2)
self.logMsg("RandomMovieMB3." + str(item_count) + ".Plot = " + plot, level=2)
self.logMsg("RandomMovieMB3." + str(item_count) + ".Year = " + str(year), level=2)
self.logMsg("RandomMovieMB3." + str(item_count) + ".Runtime = " + str(runtime), level=2)
WINDOW.setProperty("RandomMovieMB3." + str(item_count) + ".Title", title)
WINDOW.setProperty("RandomMovieMB3." + str(item_count) + ".Thumb", thumbnail)
WINDOW.setProperty("RandomMovieMB3." + str(item_count) + ".Path", playUrl)
WINDOW.setProperty("RandomMovieMB3." + str(item_count) + ".Art(fanart)", fanart)
WINDOW.setProperty("RandomMovieMB3." + str(item_count) + ".Art(medium_fanart)", medium_fanart)
WINDOW.setProperty("RandomMovieMB3." + str(item_count) + ".Art(clearlogo)", logo)
WINDOW.setProperty("RandomMovieMB3." + str(item_count) + ".Art(poster)", thumbnail)
WINDOW.setProperty("RandomMovieMB3." + str(item_count) + ".RealThumb", realthumb)
WINDOW.setProperty("RandomMovieMB3." + str(item_count) + ".Rating", str(rating))
WINDOW.setProperty("RandomMovieMB3." + str(item_count) + ".Mpaa", str(officialrating))
WINDOW.setProperty("RandomMovieMB3." + str(item_count) + ".CriticRating", str(criticrating))
WINDOW.setProperty("RandomMovieMB3." + str(item_count) + ".CriticRatingSummary", criticratingsummary)
WINDOW.setProperty("RandomMovieMB3." + str(item_count) + ".Plot", plot)
WINDOW.setProperty("RandomMovieMB3." + str(item_count) + ".Year", str(year))
WINDOW.setProperty("RandomMovieMB3." + str(item_count) + ".Runtime", str(runtime))
WINDOW.setProperty("RandomMovieMB3.Enabled", "true")
item_count = item_count + 1
self.logMsg("Updating Random TV Show List")
randomUrl = "http://" + mb3Host + ":" + mb3Port + "/mediabrowser/Users/" + userid + "/Items?Limit=10&Recursive=true&SortBy=Random&Fields=Path,Genres,MediaStreams,Overview&SortOrder=Descending&Filters=IsUnplayed,IsNotFolder&IsVirtualUnaired=false&IsMissing=False&IncludeItemTypes=Episode&format=json"
jsonData = downloadUtils.downloadUrl(randomUrl, suppress=False, popup=1 )
result = json.loads(jsonData)
self.logMsg("Random TV Show Json Data : " + str(result), level=2)
result = result.get("Items")
if(result == None):
result = []
item_count = 1
for item in result:
title = "Missing Title"
if(item.get("Name") != None):
title = item.get("Name").encode('utf-8')
seriesName = "Missing Name"
if(item.get("SeriesName") != None):
seriesName = item.get("SeriesName").encode('utf-8')
eppNumber = "X"
tempEpisodeNumber = ""
if(item.get("IndexNumber") != None):
eppNumber = item.get("IndexNumber")
if eppNumber < 10:
tempEpisodeNumber = "0" + str(eppNumber)
else:
tempEpisodeNumber = str(eppNumber)
seasonNumber = item.get("ParentIndexNumber")
if seasonNumber < 10:
tempSeasonNumber = "0" + str(seasonNumber)
else:
tempSeasonNumber = str(seasonNumber)
rating = str(item.get("CommunityRating"))
plot = item.get("Overview")
if plot == None:
plot=''
plot=plot.encode('utf-8')
item_id = item.get("Id")
poster = downloadUtils.getArtwork(item, "SeriesPrimary")
thumbnail = downloadUtils.getArtwork(item, "Primary")
logo = downloadUtils.getArtwork(item, "Logo")
fanart = downloadUtils.getArtwork(item, "Backdrop")
medium_fanart = downloadUtils.getArtwork(item, "Backdrop3")
banner = downloadUtils.getArtwork(item, "Banner")
if item.get("SeriesThumbImageTag") != None:
seriesthumbnail = downloadUtils.getArtwork(item, "Thumb3")
else:
seriesthumbnail = fanart
url = mb3Host + ":" + mb3Port + ',;' + item_id
playUrl = "plugin://plugin.video.xbmb3c/?url=" + url + '&mode=' + str(_MODE_BASICPLAY)
playUrl = playUrl.replace("\\\\","smb://")
playUrl = playUrl.replace("\\","/")
self.logMsg("RandomEpisodeMB3." + str(item_count) + ".EpisodeTitle = " + title, level=2)
self.logMsg("RandomEpisodeMB3." + str(item_count) + ".ShowTitle = " + seriesName, level=2)
self.logMsg("RandomEpisodeMB3." + str(item_count) + ".EpisodeNo = " + tempEpisodeNumber, level=2)
self.logMsg("RandomEpisodeMB3." + str(item_count) + ".SeasonNo = " + tempSeasonNumber, level=2)
self.logMsg("RandomEpisodeMB3." + str(item_count) + ".Thumb = " + thumbnail, level=2)
self.logMsg("RandomEpisodeMB3." + str(item_count) + ".Path = " + playUrl, level=2)
self.logMsg("RandomEpisodeMB3." + str(item_count) + ".Rating = " + rating, level=2)
self.logMsg("RandomEpisodeMB3." + str(item_count) + ".Art(tvshow.fanart) = " + fanart, level=2)
self.logMsg("RandomEpisodeMB3." + str(item_count) + ".Art(tvshow.clearlogo) = " + logo, level=2)
self.logMsg("RandomEpisodeMB3." + str(item_count) + ".Art(tvshow.banner) = " + banner, level=2)
self.logMsg("RandomEpisodeMB3." + str(item_count) + ".Art(tvshow.poster) = " + poster, level=2)
self.logMsg("RandomEpisodeMB3." + str(item_count) + ".Plot = " + plot, level=2)
WINDOW.setProperty("RandomEpisodeMB3." + str(item_count) + ".EpisodeTitle", title)
WINDOW.setProperty("RandomEpisodeMB3." + str(item_count) + ".ShowTitle", seriesName)
WINDOW.setProperty("RandomEpisodeMB3." + str(item_count) + ".EpisodeNo", tempEpisodeNumber)
WINDOW.setProperty("RandomEpisodeMB3." + str(item_count) + ".SeasonNo", tempSeasonNumber)
WINDOW.setProperty("RandomEpisodeMB3." + str(item_count) + ".Thumb", thumbnail)
WINDOW.setProperty("RandomEpisodeMB3." + str(item_count) + ".SeriesThumb", seriesthumbnail)
WINDOW.setProperty("RandomEpisodeMB3." + str(item_count) + ".Path", playUrl)
WINDOW.setProperty("RandomEpisodeMB3." + str(item_count) + ".Rating", rating)
WINDOW.setProperty("RandomEpisodeMB3." + str(item_count) + ".Art(tvshow.fanart)", fanart)
WINDOW.setProperty("RandomEpisodeMB3." + str(item_count) + ".Art(tvshow.medium_fanart)", medium_fanart)
WINDOW.setProperty("RandomEpisodeMB3." + str(item_count) + ".Art(tvshow.clearlogo)", logo)
WINDOW.setProperty("RandomEpisodeMB3." + str(item_count) + ".Art(tvshow.banner)", banner)
WINDOW.setProperty("RandomEpisodeMB3." + str(item_count) + ".Art(tvshow.poster)", poster)
WINDOW.setProperty("RandomEpisodeMB3." + str(item_count) + ".Plot", plot)
WINDOW.setProperty("RandomEpisodeMB3.Enabled", "true")
item_count = item_count + 1
# update random music
self.logMsg("Updating Random MusicList")
randomUrl = "http://" + mb3Host + ":" + mb3Port + "/mediabrowser/Users/" + userid + "/Items?Limit=30&Recursive=true&SortBy=Random&Fields=Path,Genres,MediaStreams,Overview&SortOrder=Descending&Filters=IsUnplayed,IsFolder&IsVirtualUnaired=false&IsMissing=False&IncludeItemTypes=MusicAlbum&format=json"
jsonData = downloadUtils.downloadUrl(randomUrl, suppress=False, popup=1 )
result = json.loads(jsonData)
self.logMsg("Random MusicList Json Data : " + str(result), level=2)
result = result.get("Items")
if(result == None):
result = []
item_count = 1
for item in result:
title = "Missing Title"
if(item.get("Name") != None):
title = item.get("Name").encode('utf-8')
artist = "Missing Artist"
if(item.get("AlbumArtist") != None):
artist = item.get("AlbumArtist").encode('utf-8')
year = "0000"
if(item.get("ProductionYear") != None):
year = str(item.get("ProductionYear"))
plot = "Missing Plot"
if(item.get("Overview") != None):
plot = item.get("Overview").encode('utf-8')
item_id = item.get("Id")
if item.get("Type") == "MusicAlbum":
parentId = item.get("ParentLogoItemId")
thumbnail = downloadUtils.getArtwork(item, "Primary")
logo = downloadUtils.getArtwork(item, "Logo")
fanart = downloadUtils.getArtwork(item, "Backdrop")
banner = downloadUtils.getArtwork(item, "Banner")
url = mb3Host + ":" + mb3Port + ',;' + item_id
playUrl = "plugin://plugin.video.xbmb3c/?url=" + url + '&mode=' + str(_MODE_BASICPLAY)
playUrl = playUrl.replace("\\\\","smb://")
playUrl = playUrl.replace("\\","/")
self.logMsg("RandomAlbumMB3." + str(item_count) + ".Title = " + title, level=2)
self.logMsg("RandomAlbumMB3." + str(item_count) + ".Artist = " + artist, level=2)
self.logMsg("RandomAlbumMB3." + str(item_count) + ".Year = " + year, level=2)
self.logMsg("RandomAlbumMB3." + str(item_count) + ".Thumb = " + thumbnail, level=2)
self.logMsg("RandomAlbumMB3." + str(item_count) + ".Path = " + playUrl, level=2)
self.logMsg("RandomAlbumMB3." + str(item_count) + ".Art(fanart) = " + fanart, level=2)
self.logMsg("RandomAlbumMB3." + str(item_count) + ".Art(clearlogo) = " + logo, level=2)
self.logMsg("RandomAlbumMB3." + str(item_count) + ".Art(banner) = " + banner, level=2)
self.logMsg("RandomAlbumMB3." + str(item_count) + ".Art(poster) = " + thumbnail, level=2)
self.logMsg("RandomAlbumMB3." + str(item_count) + ".Plot = " + plot, level=2)
WINDOW.setProperty("RandomAlbumMB3." + str(item_count) + ".Title", title)
WINDOW.setProperty("RandomAlbumMB3." + str(item_count) + ".Artist", artist)
WINDOW.setProperty("RandomAlbumMB3." + str(item_count) + ".Year", year)
WINDOW.setProperty("RandomAlbumMB3." + str(item_count) + ".Thumb", thumbnail)
WINDOW.setProperty("RandomAlbumMB3." + str(item_count) + ".Path", playUrl)
WINDOW.setProperty("RandomAlbumMB3." + str(item_count) + ".Rating", rating)
WINDOW.setProperty("RandomAlbumMB3." + str(item_count) + ".Art(fanart)", fanart)
WINDOW.setProperty("RandomAlbumMB3." + str(item_count) + ".Art(clearlogo)", logo)
WINDOW.setProperty("RandomAlbumMB3." + str(item_count) + ".Art(banner)", banner)
WINDOW.setProperty("RandomAlbumMB3." + str(item_count) + ".Art(poster)", thumbnail)
WINDOW.setProperty("RandomAlbumMB3." + str(item_count) + ".Plot", plot)
WINDOW.setProperty("RandomAlbumMB3.Enabled", "true")
item_count = item_count + 1

View File

@ -0,0 +1,564 @@
#################################################################################################
# Recent Info Updater
#################################################################################################
import xbmc
import xbmcgui
import xbmcaddon
import json
import threading
from datetime import datetime
import urllib
from DownloadUtils import DownloadUtils
_MODE_BASICPLAY=12
#define our global download utils
downloadUtils = DownloadUtils()
class RecentInfoUpdaterThread(threading.Thread):
logLevel = 0
def __init__(self, *args):
addonSettings = xbmcaddon.Addon(id='plugin.video.xbmb3c')
level = addonSettings.getSetting('logLevel')
self.logLevel = 0
if(level != None):
self.logLevel = int(level)
xbmc.log("XBMB3C RecentInfoUpdaterThread -> Log Level:" + str(self.logLevel))
threading.Thread.__init__(self, *args)
def logMsg(self, msg, level = 1):
if(self.logLevel >= level):
xbmc.log("XBMB3C RecentInfoUpdaterThread -> " + msg)
def run(self):
self.logMsg("Started")
self.updateRecent()
lastRun = datetime.today()
lastProfilePath = xbmc.translatePath('special://profile')
while (xbmc.abortRequested == False):
td = datetime.today() - lastRun
secTotal = td.seconds
profilePath = xbmc.translatePath('special://profile')
updateInterval = 60
if (xbmc.Player().isPlaying()):
updateInterval = 300
if(secTotal > updateInterval or lastProfilePath != profilePath):
self.updateRecent()
lastRun = datetime.today()
lastProfilePath = profilePath
xbmc.sleep(3000)
self.logMsg("Exited")
def updateRecent(self):
self.logMsg("updateRecent Called")
addonSettings = xbmcaddon.Addon(id='plugin.video.xbmb3c')
mb3Host = addonSettings.getSetting('ipaddress')
mb3Port = addonSettings.getSetting('port')
userName = addonSettings.getSetting('username')
userid = downloadUtils.getUserId()
self.logMsg("UserName : " + userName + " UserID : " + userid)
self.logMsg("Updating Recent Movie List")
recentUrl = "http://" + mb3Host + ":" + mb3Port + "/mediabrowser/Users/" + userid + "/Items?Limit=30&Recursive=true&SortBy=DateCreated&Fields=Path,Genres,MediaStreams,Overview,CriticRatingSummary&SortOrder=Descending&Filters=IsUnplayed,IsNotFolder&IncludeItemTypes=Movie&format=json"
jsonData = downloadUtils.downloadUrl(recentUrl, suppress=False, popup=1 )
result = json.loads(jsonData)
self.logMsg("Recent Movie Json Data : " + str(result), level=2)
result = result.get("Items")
if(result == None):
result = []
WINDOW = xbmcgui.Window( 10000 )
item_count = 1
for item in result:
title = "Missing Title"
if(item.get("Name") != None):
title = item.get("Name").encode('utf-8')
rating = item.get("CommunityRating")
criticrating = item.get("CriticRating")
officialrating = item.get("OfficialRating")
criticratingsummary = ""
if(item.get("CriticRatingSummary") != None):
criticratingsummary = item.get("CriticRatingSummary").encode('utf-8')
plot = item.get("Overview")
if plot == None:
plot=''
plot=plot.encode('utf-8')
year = item.get("ProductionYear")
if(item.get("RunTimeTicks") != None):
runtime = str(int(item.get("RunTimeTicks"))/(10000000*60))
else:
runtime = "0"
item_id = item.get("Id")
thumbnail = downloadUtils.getArtwork(item, "Primary")
logo = downloadUtils.getArtwork(item, "Logo")
fanart = downloadUtils.getArtwork(item, "Backdrop")
url = mb3Host + ":" + mb3Port + ',;' + item_id
playUrl = "plugin://plugin.video.xbmb3c/?url=" + url + '&mode=' + str(_MODE_BASICPLAY)
playUrl = playUrl.replace("\\\\","smb://")
playUrl = playUrl.replace("\\","/")
self.logMsg("LatestMovieMB3." + str(item_count) + ".Title = " + title, level=2)
self.logMsg("LatestMovieMB3." + str(item_count) + ".Thumb = " + thumbnail, level=2)
self.logMsg("LatestMovieMB3." + str(item_count) + ".Path = " + playUrl, level=2)
self.logMsg("LatestMovieMB3." + str(item_count) + ".Art(fanart) = " + fanart, level=2)
self.logMsg("LatestMovieMB3." + str(item_count) + ".Art(clearlogo) = " + logo, level=2)
self.logMsg("LatestMovieMB3." + str(item_count) + ".Art(poster) = " + thumbnail, level=2)
self.logMsg("LatestMovieMB3." + str(item_count) + ".Rating = " + str(rating), level=2)
self.logMsg("LatestMovieMB3." + str(item_count) + ".CriticRating = " + str(criticrating), level=2)
self.logMsg("LatestMovieMB3." + str(item_count) + ".CriticRatingSummary = " + criticratingsummary, level=2)
self.logMsg("LatestMovieMB3." + str(item_count) + ".Plot = " + plot, level=2)
self.logMsg("LatestMovieMB3." + str(item_count) + ".Year = " + str(year), level=2)
self.logMsg("LatestMovieMB3." + str(item_count) + ".Runtime = " + str(runtime), level=2)
WINDOW.setProperty("LatestMovieMB3." + str(item_count) + ".Title", title)
WINDOW.setProperty("LatestMovieMB3." + str(item_count) + ".Thumb", thumbnail)
WINDOW.setProperty("LatestMovieMB3." + str(item_count) + ".Path", playUrl)
WINDOW.setProperty("LatestMovieMB3." + str(item_count) + ".Art(fanart)", fanart)
WINDOW.setProperty("LatestMovieMB3." + str(item_count) + ".Art(clearlogo)", logo)
WINDOW.setProperty("LatestMovieMB3." + str(item_count) + ".Art(poster)", thumbnail)
WINDOW.setProperty("LatestMovieMB3." + str(item_count) + ".Rating", str(rating))
WINDOW.setProperty("LatestMovieMB3." + str(item_count) + ".Mpaa", str(officialrating))
WINDOW.setProperty("LatestMovieMB3." + str(item_count) + ".CriticRating", str(criticrating))
WINDOW.setProperty("LatestMovieMB3." + str(item_count) + ".CriticRatingSummary", criticratingsummary)
WINDOW.setProperty("LatestMovieMB3." + str(item_count) + ".Plot", plot)
WINDOW.setProperty("LatestMovieMB3." + str(item_count) + ".Year", str(year))
WINDOW.setProperty("LatestMovieMB3." + str(item_count) + ".Runtime", str(runtime))
WINDOW.setProperty("LatestMovieMB3.Enabled", "true")
item_count = item_count + 1
#Updating Recent Unplayed Movie List
self.logMsg("Updating Recent Unplayed Movie List")
recentUrl = "http://" + mb3Host + ":" + mb3Port + "/mediabrowser/Users/" + userid + "/Items/Latest?Limit=30&SortBy=DateCreated&Fields=Path,Genres,MediaStreams,Overview,CriticRatingSummary&IsPlayed=false&IncludeItemTypes=Movie&format=json"
jsonData = downloadUtils.downloadUrl(recentUrl, suppress=False, popup=1 )
result = json.loads(jsonData)
self.logMsg("Recent Unplayed Movie Json Data : " + str(result), level=2)
if(result == None):
result = []
WINDOW = xbmcgui.Window( 10000 )
item_count = 1
for item in result:
title = "Missing Title"
if(item.get("Name") != None):
title = item.get("Name").encode('utf-8')
rating = item.get("CommunityRating")
criticrating = item.get("CriticRating")
officialrating = item.get("OfficialRating")
criticratingsummary = ""
if(item.get("CriticRatingSummary") != None):
criticratingsummary = item.get("CriticRatingSummary").encode('utf-8')
plot = item.get("Overview")
if plot == None:
plot=''
plot=plot.encode('utf-8')
year = item.get("ProductionYear")
if(item.get("RunTimeTicks") != None):
runtime = str(int(item.get("RunTimeTicks"))/(10000000*60))
else:
runtime = "0"
item_id = item.get("Id")
thumbnail = downloadUtils.getArtwork(item, "Primary")
logo = downloadUtils.getArtwork(item, "Logo")
fanart = downloadUtils.getArtwork(item, "Backdrop")
medium_fanart = downloadUtils.getArtwork(item, "Backdrop3")
if (item.get("ImageTags") != None and item.get("ImageTags").get("Thumb") != None):
realthumb = downloadUtils.getArtwork(item, "Thumb3")
else:
realthumb = fanart
url = mb3Host + ":" + mb3Port + ',;' + item_id
playUrl = "plugin://plugin.video.xbmb3c/?url=" + url + '&mode=' + str(_MODE_BASICPLAY)
playUrl = playUrl.replace("\\\\","smb://")
playUrl = playUrl.replace("\\","/")
self.logMsg("LatestUnplayedMovieMB3." + str(item_count) + ".Title = " + title, level=2)
self.logMsg("LatestUnplayedMovieMB3." + str(item_count) + ".Thumb = " + thumbnail, level=2)
self.logMsg("LatestUnplayedMovieMB3." + str(item_count) + ".Path = " + playUrl, level=2)
self.logMsg("LatestUnplayedMovieMB3." + str(item_count) + ".Art(fanart) = " + fanart, level=2)
self.logMsg("LatestUnplayedMovieMB3." + str(item_count) + ".Art(clearlogo) = " + logo, level=2)
self.logMsg("LatestUnplayedMovieMB3." + str(item_count) + ".Art(poster) = " + thumbnail, level=2)
self.logMsg("LatestUnplayedMovieMB3." + str(item_count) + ".Rating = " + str(rating), level=2)
self.logMsg("LatestUnplayedMovieMB3." + str(item_count) + ".CriticRating = " + str(criticrating), level=2)
self.logMsg("LatestUnplayedMovieMB3." + str(item_count) + ".CriticRatingSummary = " + criticratingsummary, level=2)
self.logMsg("LatestUnplayedMovieMB3." + str(item_count) + ".Plot = " + plot, level=2)
self.logMsg("LatestUnplayedMovieMB3." + str(item_count) + ".Year = " + str(year), level=2)
self.logMsg("LatestUnplayedMovieMB3." + str(item_count) + ".Runtime = " + str(runtime), level=2)
WINDOW.setProperty("LatestUnplayedMovieMB3." + str(item_count) + ".Title", title)
WINDOW.setProperty("LatestUnplayedMovieMB3." + str(item_count) + ".Thumb", thumbnail)
WINDOW.setProperty("LatestUnplayedMovieMB3." + str(item_count) + ".Path", playUrl)
WINDOW.setProperty("LatestUnplayedMovieMB3." + str(item_count) + ".Art(fanart)", fanart)
WINDOW.setProperty("LatestUnplayedMovieMB3." + str(item_count) + ".Art(medium_fanart)", medium_fanart)
WINDOW.setProperty("LatestUnplayedMovieMB3." + str(item_count) + ".Art(clearlogo)", logo)
WINDOW.setProperty("LatestUnplayedMovieMB3." + str(item_count) + ".Art(poster)", thumbnail)
WINDOW.setProperty("LatestUnplayedMovieMB3." + str(item_count) + ".RealThumb", realthumb)
WINDOW.setProperty("LatestUnplayedMovieMB3." + str(item_count) + ".Rating", str(rating))
WINDOW.setProperty("LatestUnplayedMovieMB3." + str(item_count) + ".Mpaa", str(officialrating))
WINDOW.setProperty("LatestUnplayedMovieMB3." + str(item_count) + ".CriticRating", str(criticrating))
WINDOW.setProperty("LatestUnplayedMovieMB3." + str(item_count) + ".CriticRatingSummary", criticratingsummary)
WINDOW.setProperty("LatestUnplayedMovieMB3." + str(item_count) + ".Plot", plot)
WINDOW.setProperty("LatestUnplayedMovieMB3." + str(item_count) + ".Year", str(year))
WINDOW.setProperty("LatestUnplayedMovieMB3." + str(item_count) + ".Runtime", str(runtime))
WINDOW.setProperty("LatestUnplayedMovieMB3.Enabled", "true")
item_count = item_count + 1
#Updating Recent TV Show List
self.logMsg("Updating Recent TV Show List")
recentUrl = "http://" + mb3Host + ":" + mb3Port + "/mediabrowser/Users/" + userid + "/Items?Limit=30&Recursive=true&SortBy=DateCreated&Fields=Path,Genres,MediaStreams,Overview&SortOrder=Descending&Filters=IsUnplayed,IsNotFolder&IsVirtualUnaired=false&IsMissing=False&IncludeItemTypes=Episode&format=json"
jsonData = downloadUtils.downloadUrl(recentUrl, suppress=False, popup=1 )
result = json.loads(jsonData)
self.logMsg("Recent TV Show Json Data : " + str(result), level=2)
result = result.get("Items")
if(result == None):
result = []
item_count = 1
for item in result:
title = "Missing Title"
if(item.get("Name") != None):
title = item.get("Name").encode('utf-8')
seriesName = "Missing Name"
if(item.get("SeriesName") != None):
seriesName = item.get("SeriesName").encode('utf-8')
eppNumber = "X"
tempEpisodeNumber = "00"
if(item.get("IndexNumber") != None):
eppNumber = item.get("IndexNumber")
if eppNumber < 10:
tempEpisodeNumber = "0" + str(eppNumber)
else:
tempEpisodeNumber = str(eppNumber)
seasonNumber = item.get("ParentIndexNumber")
if seasonNumber < 10:
tempSeasonNumber = "0" + str(seasonNumber)
else:
tempSeasonNumber = str(seasonNumber)
rating = str(item.get("CommunityRating"))
plot = item.get("Overview")
if plot == None:
plot=''
plot=plot.encode('utf-8')
item_id = item.get("Id")
if item.get("Type") == "Episode" or item.get("Type") == "Season":
series_id = item.get("SeriesId")
poster = downloadUtils.getArtwork(item, "SeriesPrimary")
thumbnail = downloadUtils.getArtwork(item, "Primary")
logo = downloadUtils.getArtwork(item, "Logo")
fanart = downloadUtils.getArtwork(item, "Backdrop")
banner = downloadUtils.getArtwork(item, "Banner")
if item.get("SeriesThumbImageTag") != None:
seriesthumbnail = downloadUtils.getArtwork(item, "Thumb3")
else:
seriesthumbnail = fanart
url = mb3Host + ":" + mb3Port + ',;' + item_id
playUrl = "plugin://plugin.video.xbmb3c/?url=" + url + '&mode=' + str(_MODE_BASICPLAY)
playUrl = playUrl.replace("\\\\","smb://")
playUrl = playUrl.replace("\\","/")
self.logMsg("LatestEpisodeMB3." + str(item_count) + ".EpisodeTitle = " + title, level=2)
self.logMsg("LatestEpisodeMB3." + str(item_count) + ".ShowTitle = " + seriesName, level=2)
self.logMsg("LatestEpisodeMB3." + str(item_count) + ".EpisodeNo = " + tempEpisodeNumber, level=2)
self.logMsg("LatestEpisodeMB3." + str(item_count) + ".SeasonNo = " + tempSeasonNumber, level=2)
self.logMsg("LatestEpisodeMB3." + str(item_count) + ".Thumb = " + thumbnail, level=2)
self.logMsg("LatestEpisodeMB3." + str(item_count) + ".Path = " + playUrl, level=2)
self.logMsg("LatestEpisodeMB3." + str(item_count) + ".Rating = " + rating, level=2)
self.logMsg("LatestEpisodeMB3." + str(item_count) + ".Art(tvshow.fanart) = " + fanart, level=2)
self.logMsg("LatestEpisodeMB3." + str(item_count) + ".Art(tvshow.clearlogo) = " + logo, level=2)
self.logMsg("LatestEpisodeMB3." + str(item_count) + ".Art(tvshow.banner) = " + banner, level=2)
self.logMsg("LatestEpisodeMB3." + str(item_count) + ".Art(tvshow.poster) = " + poster, level=2)
self.logMsg("LatestEpisodeMB3." + str(item_count) + ".Plot = " + plot, level=2)
WINDOW.setProperty("LatestEpisodeMB3." + str(item_count) + ".EpisodeTitle", title)
WINDOW.setProperty("LatestEpisodeMB3." + str(item_count) + ".ShowTitle", seriesName)
WINDOW.setProperty("LatestEpisodeMB3." + str(item_count) + ".EpisodeNo", tempEpisodeNumber)
WINDOW.setProperty("LatestEpisodeMB3." + str(item_count) + ".SeasonNo", tempSeasonNumber)
WINDOW.setProperty("LatestEpisodeMB3." + str(item_count) + ".Thumb", thumbnail)
WINDOW.setProperty("LatestEpisodeMB3." + str(item_count) + ".SeriesThumb", seriesthumbnail)
WINDOW.setProperty("LatestEpisodeMB3." + str(item_count) + ".Path", playUrl)
WINDOW.setProperty("LatestEpisodeMB3." + str(item_count) + ".Rating", rating)
WINDOW.setProperty("LatestEpisodeMB3." + str(item_count) + ".Art(tvshow.fanart)", fanart)
WINDOW.setProperty("LatestEpisodeMB3." + str(item_count) + ".Art(tvshow.clearlogo)", logo)
WINDOW.setProperty("LatestEpisodeMB3." + str(item_count) + ".Art(tvshow.banner)", banner)
WINDOW.setProperty("LatestEpisodeMB3." + str(item_count) + ".Art(tvshow.poster)", poster)
WINDOW.setProperty("LatestEpisodeMB3." + str(item_count) + ".Plot", plot)
WINDOW.setProperty("LatestEpisodeMB3.Enabled", "true")
item_count = item_count + 1
#Updating Recent Unplayed TV Show List
self.logMsg("Updating Recent Unplayed TV Show List")
recentUrl = "http://" + mb3Host + ":" + mb3Port + "/mediabrowser/Users/" + userid + "/Items/Latest?Limit=30&SortBy=DateCreated&Fields=Path,Genres,MediaStreams,Overview&IsPlayed=false&GroupItems=false&IncludeItemTypes=Episode&format=json"
jsonData = downloadUtils.downloadUrl(recentUrl, suppress=False, popup=1 )
result = json.loads(jsonData)
self.logMsg("Recent Unplayed TV Show Json Data : " + str(result), level=2)
if(result == None):
result = []
item_count = 1
for item in result:
title = "Missing Title"
if(item.get("Name") != None):
title = item.get("Name").encode('utf-8')
seriesName = "Missing Name"
if(item.get("SeriesName") != None):
seriesName = item.get("SeriesName").encode('utf-8')
eppNumber = "X"
tempEpisodeNumber = "00"
if(item.get("IndexNumber") != None):
eppNumber = item.get("IndexNumber")
if eppNumber < 10:
tempEpisodeNumber = "0" + str(eppNumber)
else:
tempEpisodeNumber = str(eppNumber)
seasonNumber = item.get("ParentIndexNumber")
if seasonNumber < 10:
tempSeasonNumber = "0" + str(seasonNumber)
else:
tempSeasonNumber = str(seasonNumber)
rating = str(item.get("CommunityRating"))
plot = item.get("Overview")
if plot == None:
plot=''
plot=plot.encode('utf-8')
item_id = item.get("Id")
if item.get("Type") == "Episode" or item.get("Type") == "Season":
series_id = item.get("SeriesId")
poster = downloadUtils.getArtwork(item, "SeriesPrimary")
thumbnail = downloadUtils.getArtwork(item, "Primary")
logo = downloadUtils.getArtwork(item, "Logo")
fanart = downloadUtils.getArtwork(item, "Backdrop")
medium_fanart = downloadUtils.getArtwork(item, "Backdrop3")
banner = downloadUtils.getArtwork(item, "Banner")
if item.get("SeriesThumbImageTag") != None:
seriesthumbnail = downloadUtils.getArtwork(item, "Thumb3")
else:
seriesthumbnail = fanart
url = mb3Host + ":" + mb3Port + ',;' + item_id
playUrl = "plugin://plugin.video.xbmb3c/?url=" + url + '&mode=' + str(_MODE_BASICPLAY)
playUrl = playUrl.replace("\\\\","smb://")
playUrl = playUrl.replace("\\","/")
self.logMsg("LatestUnplayedEpisodeMB3." + str(item_count) + ".EpisodeTitle = " + title, level=2)
self.logMsg("LatestUnplayedEpisodeMB3." + str(item_count) + ".ShowTitle = " + seriesName, level=2)
self.logMsg("LatestUnplayedEpisodeMB3." + str(item_count) + ".EpisodeNo = " + tempEpisodeNumber, level=2)
self.logMsg("LatestUnplayedEpisodeMB3." + str(item_count) + ".SeasonNo = " + tempSeasonNumber, level=2)
self.logMsg("LatestUnplayedEpisodeMB3." + str(item_count) + ".Thumb = " + thumbnail, level=2)
self.logMsg("LatestUnplayedEpisodeMB3." + str(item_count) + ".Path = " + playUrl, level=2)
self.logMsg("LatestUnplayedEpisodeMB3." + str(item_count) + ".Rating = " + rating, level=2)
self.logMsg("LatestUnplayedEpisodeMB3." + str(item_count) + ".Art(tvshow.fanart) = " + fanart, level=2)
self.logMsg("LatestUnplayedEpisodeMB3." + str(item_count) + ".Art(tvshow.clearlogo) = " + logo, level=2)
self.logMsg("LatestUnplayedEpisodeMB3." + str(item_count) + ".Art(tvshow.banner) = " + banner, level=2)
self.logMsg("LatestUnplayedEpisodeMB3." + str(item_count) + ".Art(tvshow.poster) = " + poster, level=2)
self.logMsg("LatestUnplayedEpisodeMB3." + str(item_count) + ".Plot = " + plot, level=2)
WINDOW.setProperty("LatestUnplayedEpisodeMB3." + str(item_count) + ".EpisodeTitle", title)
WINDOW.setProperty("LatestUnplayedEpisodeMB3." + str(item_count) + ".ShowTitle", seriesName)
WINDOW.setProperty("LatestUnplayedEpisodeMB3." + str(item_count) + ".EpisodeNo", tempEpisodeNumber)
WINDOW.setProperty("LatestUnplayedEpisodeMB3." + str(item_count) + ".SeasonNo", tempSeasonNumber)
WINDOW.setProperty("LatestUnplayedEpisodeMB3." + str(item_count) + ".Thumb", thumbnail)
WINDOW.setProperty("LatestUnplayedEpisodeMB3." + str(item_count) + ".SeriesThumb", seriesthumbnail)
WINDOW.setProperty("LatestUnplayedEpisodeMB3." + str(item_count) + ".Path", playUrl)
WINDOW.setProperty("LatestUnplayedEpisodeMB3." + str(item_count) + ".Rating", rating)
WINDOW.setProperty("LatestUnplayedEpisodeMB3." + str(item_count) + ".Art(tvshow.fanart)", fanart)
WINDOW.setProperty("LatestUnplayedEpisodeMB3." + str(item_count) + ".Art(tvshow.medium_fanart)", medium_fanart)
WINDOW.setProperty("LatestUnplayedEpisodeMB3." + str(item_count) + ".Art(tvshow.clearlogo)", logo)
WINDOW.setProperty("LatestUnplayedEpisodeMB3." + str(item_count) + ".Art(tvshow.banner)", banner)
WINDOW.setProperty("LatestUnplayedEpisodeMB3." + str(item_count) + ".Art(tvshow.poster)", poster)
WINDOW.setProperty("LatestUnplayedEpisodeMB3." + str(item_count) + ".Plot", plot)
WINDOW.setProperty("LatestUnplayedEpisodeMB3.Enabled", "true")
item_count = item_count + 1
#Updating Recent MusicList
self.logMsg("Updating Recent MusicList")
recentUrl = "http://" + mb3Host + ":" + mb3Port + "/mediabrowser/Users/" + userid + "/Items?Limit=10&Recursive=true&SortBy=DateCreated&Fields=Path,Genres,MediaStreams,Overview&SortOrder=Descending&Filters=IsUnplayed,IsFolder&IsVirtualUnaired=false&IsMissing=False&IncludeItemTypes=MusicAlbum&format=json"
jsonData = downloadUtils.downloadUrl(recentUrl, suppress=False, popup=1 )
result = json.loads(jsonData)
self.logMsg("Recent MusicList Json Data : " + str(result), level=2)
result = result.get("Items")
if(result == None):
result = []
item_count = 1
for item in result:
title = "Missing Title"
if(item.get("Name") != None):
title = item.get("Name").encode('utf-8')
artist = "Missing Artist"
if(item.get("AlbumArtist") != None):
artist = item.get("AlbumArtist").encode('utf-8')
year = "0000"
if(item.get("ProductionYear") != None):
year = str(item.get("ProductionYear"))
plot = "Missing Plot"
if(item.get("Overview") != None):
plot = item.get("Overview").encode('utf-8')
item_id = item.get("Id")
if item.get("Type") == "MusicAlbum":
parentId = item.get("ParentLogoItemId")
thumbnail = downloadUtils.getArtwork(item, "Primary")
logo = downloadUtils.getArtwork(item, "Logo")
fanart = downloadUtils.getArtwork(item, "Backdrop")
banner = downloadUtils.getArtwork(item, "Banner")
url = mb3Host + ":" + mb3Port + ',;' + item_id
playUrl = "plugin://plugin.video.xbmb3c/?url=" + url + '&mode=' + str(_MODE_BASICPLAY)
playUrl = playUrl.replace("\\\\","smb://")
playUrl = playUrl.replace("\\","/")
self.logMsg("LatestAlbumMB3." + str(item_count) + ".Title = " + title, level=2)
self.logMsg("LatestAlbumMB3." + str(item_count) + ".Artist = " + artist, level=2)
self.logMsg("LatestAlbumMB3." + str(item_count) + ".Year = " + year, level=2)
self.logMsg("LatestAlbumMB3." + str(item_count) + ".Thumb = " + thumbnail, level=2)
self.logMsg("LatestAlbumMB3." + str(item_count) + ".Path = " + playUrl, level=2)
self.logMsg("LatestAlbumMB3." + str(item_count) + ".Art(fanart) = " + fanart, level=2)
self.logMsg("LatestAlbumMB3." + str(item_count) + ".Art(clearlogo) = " + logo, level=2)
self.logMsg("LatestAlbumMB3." + str(item_count) + ".Art(banner) = " + banner, level=2)
self.logMsg("LatestAlbumMB3." + str(item_count) + ".Art(poster) = " + thumbnail, level=2)
self.logMsg("LatestAlbumMB3." + str(item_count) + ".Plot = " + plot, level=2)
WINDOW.setProperty("LatestAlbumMB3." + str(item_count) + ".Title", title)
WINDOW.setProperty("LatestAlbumMB3." + str(item_count) + ".Artist", artist)
WINDOW.setProperty("LatestAlbumMB3." + str(item_count) + ".Year", year)
WINDOW.setProperty("LatestAlbumMB3." + str(item_count) + ".Thumb", thumbnail)
WINDOW.setProperty("LatestAlbumMB3." + str(item_count) + ".Path", playUrl)
WINDOW.setProperty("LatestAlbumMB3." + str(item_count) + ".Rating", rating)
WINDOW.setProperty("LatestAlbumMB3." + str(item_count) + ".Art(fanart)", fanart)
WINDOW.setProperty("LatestAlbumMB3." + str(item_count) + ".Art(clearlogo)", logo)
WINDOW.setProperty("LatestAlbumMB3." + str(item_count) + ".Art(banner)", banner)
WINDOW.setProperty("LatestAlbumMB3." + str(item_count) + ".Art(poster)", thumbnail)
WINDOW.setProperty("LatestAlbumMB3." + str(item_count) + ".Plot", plot)
WINDOW.setProperty("LatestAlbumMB3.Enabled", "true")
item_count = item_count + 1
#Updating Recent Photo
self.logMsg("Updating Recent Photo")
recentUrl = "http://" + mb3Host + ":" + mb3Port + "/mediabrowser/Users/" + userid + "/Items?Limit=10&Recursive=true&SortBy=DateCreated&Fields=Path,Genres,MediaStreams,Overview&SortOrder=Descending&Filters=IsUnplayed&IsVirtualUnaired=false&IsMissing=False&IncludeItemTypes=Photo&format=json"
jsonData = downloadUtils.downloadUrl(recentUrl, suppress=False, popup=1 )
result = json.loads(jsonData)
self.logMsg("Recent Photo Json Data : " + str(result), level=2)
result = result.get("Items")
if(result == None):
result = []
item_count = 1
for item in result:
title = "Missing Title"
if(item.get("Name") != None):
title = item.get("Name").encode('utf-8')
plot = "Missing Plot"
if(item.get("Overview") != None):
plot = item.get("Overview").encode('utf-8')
item_id = item.get("Id")
thumbnail = downloadUtils.getArtwork(item, "Primary")
logo = downloadUtils.getArtwork(item, "Logo")
fanart = downloadUtils.getArtwork(item, "Backdrop")
banner = downloadUtils.getArtwork(item, "Banner")
url = mb3Host + ":" + mb3Port + ',;' + item_id
playUrl = "plugin://plugin.video.xbmb3c/?url=" + url + '&mode=' + str(_MODE_BASICPLAY)
playUrl = playUrl.replace("\\\\","smb://")
playUrl = playUrl.replace("\\","/")
self.logMsg("LatestPhotoMB3." + str(item_count) + ".Title = " + title, level=2)
self.logMsg("LatestPhotoMB3." + str(item_count) + ".Thumb = " + thumbnail, level=2)
self.logMsg("LatestPhotoMB3." + str(item_count) + ".Path = " + playUrl, level=2)
self.logMsg("LatestPhotoMB3." + str(item_count) + ".Art(fanart) = " + fanart, level=2)
self.logMsg("LatestPhotoMB3." + str(item_count) + ".Art(clearlogo) = " + logo, level=2)
self.logMsg("LatestPhotoMB3." + str(item_count) + ".Art(banner) = " + banner, level=2)
self.logMsg("LatestPhotoMB3." + str(item_count) + ".Art(poster) = " + thumbnail, level=2)
self.logMsg("LatestPhotoMB3." + str(item_count) + ".Plot = " + plot, level=2)
WINDOW.setProperty("LatestPhotoMB3." + str(item_count) + ".Title", title)
WINDOW.setProperty("LatestPhotoMB3." + str(item_count) + ".Thumb", thumbnail)
WINDOW.setProperty("LatestPhotoMB3." + str(item_count) + ".Path", playUrl)
WINDOW.setProperty("LatestPhotoMB3." + str(item_count) + ".Art(fanart)", fanart)
WINDOW.setProperty("LatestPhotoMB3." + str(item_count) + ".Art(clearlogo)", logo)
WINDOW.setProperty("LatestPhotoMB3." + str(item_count) + ".Art(banner)", banner)
WINDOW.setProperty("LatestPhotoMB3." + str(item_count) + ".Art(poster)", thumbnail)
WINDOW.setProperty("LatestPhotoMB3." + str(item_count) + ".Plot", plot)
WINDOW.setProperty("LatestPhotoMB3.Enabled", "true")
item_count = item_count + 1

View File

@ -0,0 +1,334 @@
import sys
import xbmc
import xbmcgui
import xbmcaddon
import json as json
import urllib
from DownloadUtils import DownloadUtils
import threading
_MODE_ITEM_DETAILS=17
class SearchDialog(xbmcgui.WindowXMLDialog):
searchThread = None
def __init__(self, *args, **kwargs):
xbmcgui.WindowXMLDialog.__init__(self, *args, **kwargs)
def onInit(self):
self.action_exitkeys_id = [10, 13]
self.searchThread = BackgroundSearchThread()
self.searchThread.setDialog(self)
self.searchThread.start()
def onFocus(self, controlId):
pass
def onAction(self, action):
#xbmc.log("onAction : " + str(action.getId()) + " " + str(action.getButtonCode()) + " " + str(action))
ACTION_PREVIOUS_MENU = 10
ACTION_SELECT_ITEM = 7
ACTION_PARENT_DIR = 9
if action == ACTION_PREVIOUS_MENU or action.getId() == 92:
searchTerm = self.getControl(3010).getLabel()
if(len(searchTerm) == 0):
self.close()
else:
searchTerm = searchTerm[:-1]
self.getControl(3010).setLabel(searchTerm)
self.searchThread.setSearch(searchTerm)
#self.getControl(3010).setLabel(str(action.getButtonCode()))
def closeDialog(self):
thread.stopRunning()
self.close()
def onClick(self, controlID):
if(controlID == 3020):
self.addCharacter("A")
elif(controlID == 3021):
self.addCharacter("B")
elif(controlID == 3022):
self.addCharacter("C")
elif(controlID == 3023):
self.addCharacter("D")
elif(controlID == 3024):
self.addCharacter("E")
elif(controlID == 3025):
self.addCharacter("F")
elif(controlID == 3026):
self.addCharacter("G")
elif(controlID == 3027):
self.addCharacter("H")
elif(controlID == 3028):
self.addCharacter("I")
elif(controlID == 3029):
self.addCharacter("J")
elif(controlID == 3030):
self.addCharacter("K")
elif(controlID == 3031):
self.addCharacter("L")
elif(controlID == 3032):
self.addCharacter("M")
elif(controlID == 3033):
self.addCharacter("N")
elif(controlID == 3034):
self.addCharacter("O")
elif(controlID == 3035):
self.addCharacter("P")
elif(controlID == 3036):
self.addCharacter("Q")
elif(controlID == 3037):
self.addCharacter("R")
elif(controlID == 3038):
self.addCharacter("S")
elif(controlID == 3039):
self.addCharacter("T")
elif(controlID == 3040):
self.addCharacter("U")
elif(controlID == 3041):
self.addCharacter("V")
elif(controlID == 3042):
self.addCharacter("W")
elif(controlID == 3043):
self.addCharacter("X")
elif(controlID == 3044):
self.addCharacter("Y")
elif(controlID == 3045):
self.addCharacter("Z")
elif(controlID == 3046):
self.addCharacter("0")
elif(controlID == 3047):
self.addCharacter("1")
elif(controlID == 3048):
self.addCharacter("2")
elif(controlID == 3049):
self.addCharacter("3")
elif(controlID == 3050):
self.addCharacter("4")
elif(controlID == 3051):
self.addCharacter("5")
elif(controlID == 3052):
self.addCharacter("6")
elif(controlID == 3053):
self.addCharacter("7")
elif(controlID == 3054):
self.addCharacter("8")
elif(controlID == 3055):
self.addCharacter("9")
elif(controlID == 3056):
searchTerm = self.getControl(3010).getLabel()
searchTerm = searchTerm[:-1]
self.getControl(3010).setLabel(searchTerm)
self.searchThread.setSearch(searchTerm)
elif(controlID == 3057):
self.addCharacter(" ")
elif(controlID == 3058):
self.getControl(3010).setLabel("")
self.searchThread.setSearch("")
elif(controlID == 3110):
#xbmc.executebuiltin("Dialog.Close(all,true)")
itemList = self.getControl(3110)
item = itemList.getSelectedItem()
action = item.getProperty("ActionUrl")
xbmc.executebuiltin("RunPlugin(" + action + ")")
elif(controlID == 3111):
#xbmc.executebuiltin("Dialog.Close(all,true)")
itemList = self.getControl(3111)
item = itemList.getSelectedItem()
action = item.getProperty("ActionUrl")
xbmc.executebuiltin("RunPlugin(" + action + ")")
elif(controlID == 3112):
#xbmc.executebuiltin("Dialog.Close(all,true)")
itemList = self.getControl(3112)
item = itemList.getSelectedItem()
action = item.getProperty("ActionUrl")
xbmc.executebuiltin("RunPlugin(" + action + ")")
pass
def addCharacter(self, char):
searchTerm = self.getControl(3010).getLabel()
searchTerm = searchTerm + char
self.getControl(3010).setLabel(searchTerm)
self.searchThread.setSearch(searchTerm)
class BackgroundSearchThread(threading.Thread):
active = True
searchDialog = None
searchString = ""
def __init__(self, *args):
xbmc.log("BackgroundSearchThread Init")
threading.Thread.__init__(self, *args)
def setSearch(self, searchFor):
self.searchString = searchFor
def stopRunning(self):
self.active = False
def setDialog(self, searchDialog):
self.searchDialog = searchDialog
def run(self):
xbmc.log("BackgroundSearchThread Started")
lastSearchString = ""
while(xbmc.abortRequested == False and self.active == True):
currentSearch = self.searchString
if(currentSearch != lastSearchString):
lastSearchString = currentSearch
self.doSearch(currentSearch)
xbmc.sleep(2000)
xbmc.log("BackgroundSearchThread Exited")
def doSearch(self, searchTerm):
movieResultsList = self.searchDialog.getControl(3110)
while(movieResultsList.size() > 0):
movieResultsList.removeItem(0)
#movieResultsList.reset()
seriesResultsList = self.searchDialog.getControl(3111)
while(seriesResultsList.size() > 0):
seriesResultsList.removeItem(0)
#seriesResultsList.reset()
episodeResultsList = self.searchDialog.getControl(3112)
while(episodeResultsList.size() > 0):
episodeResultsList.removeItem(0)
#episodeResultsList.reset()
if(len(searchTerm) == 0):
return
__settings__ = xbmcaddon.Addon(id='plugin.video.xbmb3c')
port = __settings__.getSetting('port')
host = __settings__.getSetting('ipaddress')
server = host + ":" + port
downloadUtils = DownloadUtils()
#
# Process movies
#
search = urllib.quote(searchTerm)
url = "http://" + server + "/mediabrowser/Search/Hints?SearchTerm=" + search + "&Limit=10&IncludeItemTypes=Movie&format=json"
jsonData = downloadUtils.downloadUrl(url, suppress=False, popup=1)
result = json.loads(jsonData)
items = result.get("SearchHints")
if(items == None or len(items) == 0):
item = []
for item in items:
xbmc.log(str(item))
item_id = item.get("ItemId")
item_name = item.get("Name")
item_type = item.get("Type")
typeLabel = "Movie"
thumbPath = downloadUtils.imageUrl(item_id, "Primary", 0, 200, 200)
xbmc.log(thumbPath)
listItem = xbmcgui.ListItem(label=item_name, label2=typeLabel, iconImage=thumbPath, thumbnailImage=thumbPath)
actionUrl = "plugin://plugin.video.xbmb3c?id=" + item_id + "&mode=" + str(_MODE_ITEM_DETAILS)
listItem.setProperty("ActionUrl", actionUrl)
movieResultsList.addItem(listItem)
#
# Process series
#
search = urllib.quote(searchTerm)
url = "http://" + server + "/mediabrowser/Search/Hints?SearchTerm=" + search + "&Limit=10&IncludeItemTypes=Series&format=json"
jsonData = downloadUtils.downloadUrl(url, suppress=False, popup=1 )
result = json.loads(jsonData)
items = result.get("SearchHints")
if(items == None or len(items) == 0):
item = []
for item in items:
xbmc.log(str(item))
item_id = item.get("ItemId")
item_name = item.get("Name")
item_type = item.get("Type")
typeLabel = ""
image_id = ""
image_id = item.get("ItemId")
typeLabel = "Series"
thumbPath = downloadUtils.imageUrl(image_id, "Primary", 0, 200, 200)
xbmc.log(thumbPath)
listItem = xbmcgui.ListItem(label=item_name, label2=typeLabel, iconImage=thumbPath, thumbnailImage=thumbPath)
actionUrl = "plugin://plugin.video.xbmb3c?id=" + item_id + "&mode=" + str(_MODE_ITEM_DETAILS)
listItem.setProperty("ActionUrl", actionUrl)
seriesResultsList.addItem(listItem)
#
# Process episodes
#
search = urllib.quote(searchTerm)
url = "http://" + server + "/mediabrowser/Search/Hints?SearchTerm=" + search + "&Limit=10&IncludeItemTypes=Episode&format=json"
jsonData = downloadUtils.downloadUrl(url, suppress=False, popup=1 )
result = json.loads(jsonData)
items = result.get("SearchHints")
if(items == None or len(items) == 0):
item = []
for item in items:
xbmc.log(str(item))
item_id = item.get("ItemId")
item_name = item.get("Name")
item_type = item.get("Type")
image_id = item.get("ThumbImageItemId")
season = item.get("ParentIndexNumber")
eppNum = item.get("IndexNumber")
typeLabel = "S" + str(season).zfill(2) + "E" + str(eppNum).zfill(2)
thumbPath = downloadUtils.imageUrl(image_id, "Primary", 0, 200, 200)
xbmc.log(thumbPath)
listItem = xbmcgui.ListItem(label=item_name, label2=typeLabel, iconImage=thumbPath, thumbnailImage=thumbPath)
actionUrl = "plugin://plugin.video.xbmb3c?id=" + item_id + "&mode=" + str(_MODE_ITEM_DETAILS)
listItem.setProperty("ActionUrl", actionUrl)
episodeResultsList.addItem(listItem)

View File

@ -0,0 +1,161 @@
#################################################################################################
# Suggested Updater
#################################################################################################
import xbmc
import xbmcgui
import xbmcaddon
import json
import threading
from datetime import datetime
import urllib
from DownloadUtils import DownloadUtils
_MODE_BASICPLAY=12
#define our global download utils
downloadUtils = DownloadUtils()
class SuggestedUpdaterThread(threading.Thread):
logLevel = 0
def __init__(self, *args):
addonSettings = xbmcaddon.Addon(id='plugin.video.xbmb3c')
level = addonSettings.getSetting('logLevel')
self.logLevel = 0
if(level != None):
self.logLevel = int(level)
xbmc.log("XBMB3C SuggestedUpdaterThread -> Log Level:" + str(self.logLevel))
threading.Thread.__init__(self, *args)
def logMsg(self, msg, level = 1):
if(self.logLevel >= level):
xbmc.log("XBMB3C SuggestedUpdaterThread -> " + msg)
def run(self):
self.logMsg("Started")
self.updateSuggested()
lastRun = datetime.today()
while (xbmc.abortRequested == False):
td = datetime.today() - lastRun
secTotal = td.seconds
if(secTotal > 300):
self.updateSuggested()
lastRun = datetime.today()
xbmc.sleep(3000)
self.logMsg("Exited")
def updateSuggested(self):
self.logMsg("updateSuggested Called")
addonSettings = xbmcaddon.Addon(id='plugin.video.xbmb3c')
mb3Host = addonSettings.getSetting('ipaddress')
mb3Port = addonSettings.getSetting('port')
userName = addonSettings.getSetting('username')
userid = downloadUtils.getUserId()
self.logMsg("updateSuggested UserID : " + userid)
self.logMsg("Updating Suggested List")
suggestedUrl = "http://" + mb3Host + ":" + mb3Port + "/mediabrowser/Movies/Recommendations?UserId=" + userid + "&categoryLimit=1&ItemLimit=6&format=json"
jsonData = downloadUtils.downloadUrl(suggestedUrl, suppress=False, popup=1 )
result = json.loads(jsonData)
self.logMsg("Suggested Movie Json Data : " + str(result), level=2)
basemovie = "Missing Base Title"
if(result == None or len(result) == 0):
return
if (result[0].get("BaselineItemName") != None):
basemovie = result[0].get("BaselineItemName").encode('utf-8')
result = result[0].get("Items")
WINDOW = xbmcgui.Window( 10000 )
if(result == None):
result = []
item_count = 1
for item in result:
title = "Missing Title"
if(item.get("Name") != None):
title = item.get("Name").encode('utf-8')
rating = item.get("CommunityRating")
criticrating = item.get("CriticRating")
officialrating = item.get("OfficialRating")
criticratingsummary = ""
if(item.get("CriticRatingSummary") != None):
criticratingsummary = item.get("CriticRatingSummary").encode('utf-8')
plot = item.get("Overview")
if plot == None:
plot=''
plot=plot.encode('utf-8')
year = item.get("ProductionYear")
if(item.get("RunTimeTicks") != None):
runtime = str(int(item.get("RunTimeTicks"))/(10000000*60))
else:
runtime = "0"
item_id = item.get("Id")
thumbnail = downloadUtils.getArtwork(item, "Primary")
logo = downloadUtils.getArtwork(item, "Logo")
fanart = downloadUtils.getArtwork(item, "Backdrop")
medium_fanart = downloadUtils.getArtwork(item, "Backdrop3")
if item.get("ImageTags").get("Thumb") != None:
realthumbnail = downloadUtils.getArtwork(item, "Thumb3")
else:
realthumbnail = fanart
url = mb3Host + ":" + mb3Port + ',;' + item_id
playUrl = "plugin://plugin.video.xbmb3c/?url=" + url + '&mode=' + str(_MODE_BASICPLAY)
playUrl = playUrl.replace("\\\\","smb://")
playUrl = playUrl.replace("\\","/")
self.logMsg("SuggestedMovieMB3." + str(item_count) + ".Title = " + title, level=2)
self.logMsg("SuggestedMovieMB3." + str(item_count) + ".Thumb = " + realthumbnail, level=2)
self.logMsg("SuggestedMovieMB3." + str(item_count) + ".Path = " + playUrl, level=2)
self.logMsg("SuggestedMovieMB3." + str(item_count) + ".Art(fanart) = " + fanart, level=2)
self.logMsg("SuggestedMovieMB3." + str(item_count) + ".Art(clearlogo) = " + logo, level=2)
self.logMsg("SuggestedMovieMB3." + str(item_count) + ".Art(poster) = " + thumbnail, level=2)
self.logMsg("SuggestedMovieMB3." + str(item_count) + ".Rating = " + str(rating), level=2)
self.logMsg("SuggestedMovieMB3." + str(item_count) + ".CriticRating = " + str(criticrating), level=2)
self.logMsg("SuggestedMovieMB3." + str(item_count) + ".CriticRatingSummary = " + criticratingsummary, level=2)
self.logMsg("SuggestedMovieMB3." + str(item_count) + ".Plot = " + plot, level=2)
self.logMsg("SuggestedMovieMB3." + str(item_count) + ".Year = " + str(year), level=2)
self.logMsg("SuggestedMovieMB3." + str(item_count) + ".Runtime = " + str(runtime), level=2)
self.logMsg("SuggestedMovieMB3." + str(item_count) + ".SuggestedMovieTitle = " + basemovie, level=2)
WINDOW.setProperty("SuggestedMovieMB3." + str(item_count) + ".Title", title)
WINDOW.setProperty("SuggestedMovieMB3." + str(item_count) + ".Thumb", realthumbnail)
WINDOW.setProperty("SuggestedMovieMB3." + str(item_count) + ".Path", playUrl)
WINDOW.setProperty("SuggestedMovieMB3." + str(item_count) + ".Art(fanart)", fanart)
WINDOW.setProperty("SuggestedMovieMB3." + str(item_count) + ".Art(medium_fanart)", medium_fanart)
WINDOW.setProperty("SuggestedMovieMB3." + str(item_count) + ".Art(clearlogo)", logo)
WINDOW.setProperty("SuggestedMovieMB3." + str(item_count) + ".Art(poster)", thumbnail)
WINDOW.setProperty("SuggestedMovieMB3." + str(item_count) + ".Rating", str(rating))
WINDOW.setProperty("SuggestedMovieMB3." + str(item_count) + ".Mpaa", str(officialrating))
WINDOW.setProperty("SuggestedMovieMB3." + str(item_count) + ".CriticRating", str(criticrating))
WINDOW.setProperty("SuggestedMovieMB3." + str(item_count) + ".CriticRatingSummary", criticratingsummary)
WINDOW.setProperty("SuggestedMovieMB3." + str(item_count) + ".Plot", plot)
WINDOW.setProperty("SuggestedMovieMB3." + str(item_count) + ".Year", str(year))
WINDOW.setProperty("SuggestedMovieMB3." + str(item_count) + ".Runtime", str(runtime))
WINDOW.setProperty("SuggestedMovieMB3." + str(item_count) + ".SuggestedMovieTitle", basemovie)
WINDOW.setProperty("SuggestedMovieMB3.Enabled", "true")
item_count = item_count + 1

183
resources/lib/ThemeMusic.py Normal file
View File

@ -0,0 +1,183 @@
#################################################################################################
# Start of ThemeMusic Thread
# plays theme music when applicable
#################################################################################################
import xbmc
import xbmcgui
import xbmcaddon
import json
import threading
from datetime import datetime
import urllib
import urllib2
import random
from Utils import PlayUtils
from DownloadUtils import DownloadUtils
#define our global download utils
downloadUtils = DownloadUtils()
class ThemeMusicThread(threading.Thread):
playingTheme = False
themeId = ''
volume = ''
themeMap = {}
def __init__(self, *args):
addonSettings = xbmcaddon.Addon(id='plugin.video.xbmb3c')
level = addonSettings.getSetting('logLevel')
self.logLevel = 0
if(level != None):
self.logLevel = int(level)
xbmc.log("XBMB3C ThemeMusicThread -> Log Level:" + str(self.logLevel))
threading.Thread.__init__(self, *args)
def logMsg(self, msg, level = 1):
if(self.logLevel >= level):
xbmc.log("XBMB3C ThemeMusicThread -> " + msg)
def run(self):
self.logMsg("Started")
self.updateThemeMusic()
lastRun = datetime.today()
addonSettings = xbmcaddon.Addon(id='plugin.video.xbmb3c')
themeRefresh = 2
while (xbmc.abortRequested == False):
td = datetime.today() - lastRun
secTotal = td.seconds
if (secTotal > themeRefresh):
self.updateThemeMusic()
lastRun = datetime.today()
xbmc.sleep(2000)
self.logMsg("Exited")
def updateThemeMusic(self):
self.logMsg("updateThemeMusic Called")
addonSettings = xbmcaddon.Addon(id='plugin.video.xbmb3c')
mb3Host = addonSettings.getSetting('ipaddress')
mb3Port = addonSettings.getSetting('port')
newid = xbmc.getInfoLabel('ListItem.Property(ItemGUID)')
if newid != self.themeId:
if self.isPlayingZone() and self.playingTheme == True:
if xbmc.Player().isPlayingAudio():
self.stop()
xbmc.sleep(1500)
id = xbmc.getInfoLabel('ListItem.Property(ItemGUID)')
if id != newid:
return
self.logMsg("updateThemeMusic itemGUID : " + id)
if self.isPlayingZone() and self.isChangeTheme():
self.themeId = id
themeUrl = "http://" + mb3Host + ":" + mb3Port + "/mediabrowser/Items/" + id + "/ThemeSongs?format=json"
self.logMsg("updateThemeMusic themeUrl : " + themeUrl)
if themeUrl not in self.themeMap:
jsonData = downloadUtils.downloadUrl(themeUrl, suppress=False, popup=1 )
theme = json.loads(jsonData)
if(theme == None):
theme = []
self.logMsg("updateThemeMusic added theme to map : " + themeUrl)
self.themeMap[themeUrl] = theme
elif themeUrl in self.themeMap:
theme = self.themeMap.get(themeUrl)
self.logMsg("updateThemeMusic retrieved theme from map : " + themeUrl)
themeItems = theme.get("Items")
if themeItems != []:
themePlayUrl = PlayUtils().getPlayUrl(mb3Host + ":" + mb3Port,themeItems[0].get("Id"),themeItems[0])
self.logMsg("updateThemeMusic themeMusicPath : " + str(themePlayUrl))
self.playingTheme = True
self.setVolume(60)
xbmc.Player().play(themePlayUrl)
elif themeItems == [] and self.playingTheme == True:
self.stop(True)
if not self.isPlayingZone() and self.playingTheme == True:
# stop
if xbmc.Player().isPlayingAudio():
self.stop()
def stop(self, forceStop = False):
# Only stop if playing audio
if xbmc.Player().isPlayingAudio() or forceStop == True:
self.playingTheme = False
cur_vol = self.getVolume()
# Calculate how fast to fade the theme, this determines
# the number of step to drop the volume in
numSteps = 15
vol_step = cur_vol / numSteps
# do not mute completely else the mute icon shows up
for step in range (0,(numSteps-1)):
vol = cur_vol - vol_step
self.setVolume(vol)
cur_vol = vol
xbmc.sleep(200)
xbmc.Player().stop()
self.setVolume(self.volume)
# Works out if the currently displayed area on the screen is something
# that is deemed a zone where themes should be played
def isPlayingZone(self):
if "plugin://plugin.video.xbmb3c" in xbmc.getInfoLabel( "ListItem.Path" ):
return True
# Any other area is deemed to be a non play area
return False
# Works out if we should change/start a theme
def isChangeTheme(self):
id = xbmc.getInfoLabel('ListItem.Property(ItemGUID)')
if id != "":
if self.volume == '':
self.volume = self.getVolume()
# we have something to start with
addonSettings = xbmcaddon.Addon(id='plugin.video.xbmb3c')
if addonSettings.getSetting('useThemeMusic') == "true":
# cool theme music is on continue
if id == self.themeId:
# same as before now do we need to restart
if addonSettings.getSetting('loopThemeMusic') == "true" and xbmc.Player().isPlayingAudio() == False:
return True
if id != self.themeId:
# new id return true
return True
# still here return False
return False
# This will return the volume in a range of 0-100
def getVolume(self):
result = xbmc.executeJSONRPC('{"jsonrpc": "2.0", "method": "Application.GetProperties", "params": { "properties": [ "volume" ] }, "id": 1}')
json_query = json.loads(result)
if "result" in json_query and json_query['result'].has_key('volume'):
# Get the volume value
volume = json_query['result']['volume']
return volume
# Sets the volume in the range 0-100
def setVolume(self, newvolume):
# Can't use the RPC version as that will display the volume dialog
# '{"jsonrpc": "2.0", "method": "Application.SetVolume", "params": { "volume": %d }, "id": 1}'
xbmc.executebuiltin('XBMC.SetVolume(%d)' % newvolume, True)

167
resources/lib/Utils.py Normal file
View File

@ -0,0 +1,167 @@
#################################################################################################
# utils class
#################################################################################################
import xbmc
import xbmcgui
import xbmcaddon
import json
import threading
from datetime import datetime
from DownloadUtils import DownloadUtils
import urllib
import sys
#define our global download utils
downloadUtils = DownloadUtils()
###########################################################################
class PlayUtils():
def getPlayUrl(self, server, id, result):
addonSettings = xbmcaddon.Addon(id='plugin.video.xbmb3c')
# if the path is local and depending on the video quality play we can direct play it do so-
xbmc.log("XBMB3C getPlayUrl")
if self.isDirectPlay(result) == True:
xbmc.log("XBMB3C getPlayUrl -> Direct Play")
playurl = result.get("Path")
if playurl != None:
#We have a path to play so play it
USER_AGENT = 'QuickTime/7.7.4'
# If the file it is not a media stub
if (result.get("IsPlaceHolder") != True):
if (result.get("VideoType") == "Dvd"):
playurl = playurl + "/VIDEO_TS/VIDEO_TS.IFO"
elif (result.get("VideoType") == "BluRay"):
playurl = playurl + "/BDMV/index.bdmv"
if addonSettings.getSetting('smbusername') == '':
playurl = playurl.replace("\\\\", "smb://")
else:
playurl = playurl.replace("\\\\", "smb://" + addonSettings.getSetting('smbusername') + ':' + addonSettings.getSetting('smbpassword') + '@')
playurl = playurl.replace("\\", "/")
if ("apple.com" in playurl):
playurl += '?|User-Agent=%s' % USER_AGENT
if addonSettings.getSetting('playFromStream') == "true":
playurl = 'http://' + server + '/mediabrowser/Videos/' + id + '/stream?static=true'
mediaSources = result.get("MediaSources")
if(mediaSources != None):
if mediaSources[0].get('DefaultAudioStreamIndex') != None:
playurl = playurl + "&AudioStreamIndex=" +str(mediaSources[0].get('DefaultAudioStreamIndex'))
if mediaSources[0].get('DefaultSubtitleStreamIndex') != None:
playurl = playurl + "&SubtitleStreamIndex=" + str(mediaSources[0].get('DefaultAudioStreamIndex'))
# elif self.isNetworkQualitySufficient(result) == True:
# xbmc.log("XBMB3C getPlayUrl -> Stream")
#No direct path but sufficient network so static stream
# if result.get("Type") == "Audio":
# playurl = 'http://' + server + '/mediabrowser/Audio/' + id + '/stream?static=true&mediaSourceId=' + id
#else:
# playurl = 'http://' + server + '/mediabrowser/Videos/' + id + '/stream?static=true&mediaSourceId=' + id
else:
#No path or has a path but not sufficient network so transcode
xbmc.log("XBMB3C getPlayUrl -> Transcode")
if result.get("Type") == "Audio":
playurl = 'http://' + server + '/mediabrowser/Audio/' + id + '/stream.mp3'
else:
txt_mac = downloadUtils.getMachineId()
playurl = 'http://' + server + '/mediabrowser/Videos/' + id + '/master.m3u8?mediaSourceId=' + id
playurl = playurl + '&videoCodec=h264'
playurl = playurl + '&AudioCodec=aac,ac3'
playurl = playurl + '&deviceId=' + txt_mac
playurl = playurl + '&VideoBitrate=' + str(int(self.getVideoBitRate()) * 1000)
mediaSources = result.get("MediaSources")
if(mediaSources != None):
if mediaSources[0].get('DefaultAudioStreamIndex') != None:
playurl = playurl + "&AudioStreamIndex=" +str(mediaSources[0].get('DefaultAudioStreamIndex'))
if mediaSources[0].get('DefaultSubtitleStreamIndex') != None:
playurl = playurl + "&SubtitleStreamIndex=" + str(mediaSources[0].get('DefaultAudioStreamIndex'))
return playurl.encode('utf-8')
# Works out if we are direct playing or not
def isDirectPlay(self, result):
if result.get("LocationType") == "FileSystem" and self.isNetworkQualitySufficient(result) == True and self.isLocalPath(result) == False:
return True
else:
return False
# Works out if the network quality can play directly or if transcoding is needed
def isNetworkQualitySufficient(self, result):
settingsVideoBitRate = self.getVideoBitRate()
settingsVideoBitRate = int(settingsVideoBitRate) * 1000
mediaSources = result.get("MediaSources")
if(mediaSources != None):
if mediaSources[0].get('Bitrate') != None:
if settingsVideoBitRate < int(mediaSources[0].get('Bitrate')):
xbmc.log("XBMB3C isNetworkQualitySufficient -> FALSE bit rate - settingsVideoBitRate: " + str(settingsVideoBitRate) + " mediasource bitrate: " + str(mediaSources[0].get('Bitrate')))
return False
else:
xbmc.log("XBMB3C isNetworkQualitySufficient -> TRUE bit rate")
return True
# Any thing else is ok
xbmc.log("XBMB3C isNetworkQualitySufficient -> TRUE default")
return True
# get the addon video quality
def getVideoBitRate(self):
addonSettings = xbmcaddon.Addon(id='plugin.video.xbmb3c')
videoQuality = addonSettings.getSetting('videoBitRate')
if (videoQuality == "0"):
return '664'
elif (videoQuality == "1"):
return '996'
elif (videoQuality == "2"):
return '1320'
elif (videoQuality == "3"):
return '2000'
elif (videoQuality == "4"):
return '3200'
elif (videoQuality == "5"):
return '4700'
elif (videoQuality == "6"):
return '6200'
elif (videoQuality == "7"):
return '7700'
elif (videoQuality == "8"):
return '9200'
elif (videoQuality == "9"):
return '10700'
elif (videoQuality == "10"):
return '12200'
elif (videoQuality == "11"):
return '13700'
elif (videoQuality == "12"):
return '15200'
elif (videoQuality == "13"):
return '16700'
elif (videoQuality == "14"):
return '18200'
elif (videoQuality == "15"):
return '20000'
elif (videoQuality == "16"):
return '40000'
elif (videoQuality == "17"):
return '100000'
elif (videoQuality == "18"):
return '1000000'
# Works out if the network quality can play directly or if transcoding is needed
def isLocalPath(self, result):
playurl = result.get("Path")
if playurl != None:
#We have a path to play so play it
if ":\\" in playurl:
return True
else:
return False
# default to not local
return False

View File

@ -0,0 +1,244 @@
#################################################################################################
# WebSocket Client thread
#################################################################################################
import xbmc
import xbmcgui
import xbmcaddon
import json
import threading
import urllib
import socket
import websocket
from ClientInformation import ClientInformation
_MODE_BASICPLAY=12
class WebSocketThread(threading.Thread):
logLevel = 0
client = None
keepRunning = True
def __init__(self, *args):
addonSettings = xbmcaddon.Addon(id='plugin.video.xbmb3c')
level = addonSettings.getSetting('logLevel')
self.logLevel = 0
if(level != None):
self.logLevel = int(level)
xbmc.log("XBMB3C WebSocketThread -> Log Level:" + str(self.logLevel))
threading.Thread.__init__(self, *args)
def logMsg(self, msg, level = 1):
if(self.logLevel >= level):
xbmc.log("XBMB3C WebSocketThread -> " + msg)
def playbackStarted(self, itemId):
if(self.client != None):
try:
self.logMsg("Sending Playback Started")
messageData = {}
messageData["MessageType"] = "PlaybackStart"
messageData["Data"] = itemId + "|true|audio,video"
messageString = json.dumps(messageData)
self.logMsg("Message Data : " + messageString)
self.client.send(messageString)
except Exception, e:
self.logMsg("Exception : " + str(e), level=0)
else:
self.logMsg("Sending Playback Started NO Object ERROR")
def playbackStopped(self, itemId, ticks):
if(self.client != None):
try:
self.logMsg("Sending Playback Stopped")
messageData = {}
messageData["MessageType"] = "PlaybackStopped"
messageData["Data"] = itemId + "|" + str(ticks)
messageString = json.dumps(messageData)
self.client.send(messageString)
except Exception, e:
self.logMsg("Exception : " + str(e), level=0)
else:
self.logMsg("Sending Playback Stopped NO Object ERROR")
def sendProgressUpdate(self, itemId, ticks):
if(self.client != None):
try:
self.logMsg("Sending Progress Update")
messageData = {}
messageData["MessageType"] = "PlaybackProgress"
messageData["Data"] = itemId + "|" + str(ticks) + "|false|false"
messageString = json.dumps(messageData)
self.logMsg("Message Data : " + messageString)
self.client.send(messageString)
except Exception, e:
self.logMsg("Exception : " + str(e), level=0)
else:
self.logMsg("Sending Progress Update NO Object ERROR")
def stopClient(self):
# stopping the client is tricky, first set keep_running to false and then trigger one
# more message by requesting one SessionsStart message, this causes the
# client to receive the message and then exit
if(self.client != None):
self.logMsg("Stopping Client")
self.keepRunning = False
self.client.keep_running = False
self.logMsg("Stopping Client : KeepRunning set to False")
'''
try:
self.keepRunning = False
self.client.keep_running = False
self.logMsg("Stopping Client")
self.logMsg("Calling Ping")
self.client.sock.ping()
self.logMsg("Calling Socket Shutdown()")
self.client.sock.sock.shutdown(socket.SHUT_RDWR)
self.logMsg("Calling Socket Close()")
self.client.sock.sock.close()
self.logMsg("Stopping Client Done")
self.logMsg("Calling Ping")
self.client.sock.ping()
except Exception, e:
self.logMsg("Exception : " + str(e), level=0)
'''
else:
self.logMsg("Stopping Client NO Object ERROR")
def on_message(self, ws, message):
self.logMsg("Message : " + str(message))
result = json.loads(message)
messageType = result.get("MessageType")
playCommand = result.get("PlayCommand")
data = result.get("Data")
if(messageType != None and messageType == "Play" and data != None):
itemIds = data.get("ItemIds")
playCommand = data.get("PlayCommand")
if(playCommand != None and playCommand == "PlayNow"):
startPositionTicks = data.get("StartPositionTicks")
self.logMsg("Playing Media With ID : " + itemIds[0])
addonSettings = xbmcaddon.Addon(id='plugin.video.xbmb3c')
mb3Host = addonSettings.getSetting('ipaddress')
mb3Port = addonSettings.getSetting('port')
url = mb3Host + ":" + mb3Port + ',;' + itemIds[0]
if(startPositionTicks == None):
url += ",;" + "-1"
else:
url += ",;" + str(startPositionTicks)
playUrl = "plugin://plugin.video.xbmb3c/?url=" + url + '&mode=' + str(_MODE_BASICPLAY)
playUrl = playUrl.replace("\\\\","smb://")
playUrl = playUrl.replace("\\","/")
xbmc.Player().play(playUrl)
elif(messageType != None and messageType == "Playstate"):
command = data.get("Command")
if(command != None and command == "Stop"):
self.logMsg("Playback Stopped")
xbmc.executebuiltin('xbmc.activatewindow(10000)')
xbmc.Player().stop()
if(command != None and command == "Seek"):
seekPositionTicks = data.get("SeekPositionTicks")
self.logMsg("Playback Seek : " + str(seekPositionTicks))
seekTime = (seekPositionTicks / 1000) / 10000
xbmc.Player().seekTime(seekTime)
def on_error(self, ws, error):
self.logMsg("Error : " + str(error))
def on_close(self, ws):
self.logMsg("Closed")
def on_open(self, ws):
try:
clientInfo = ClientInformation()
machineId = clientInfo.getMachineId()
version = clientInfo.getVersion()
messageData = {}
messageData["MessageType"] = "Identity"
addonSettings = xbmcaddon.Addon(id='plugin.video.xbmb3c')
deviceName = addonSettings.getSetting('deviceName')
deviceName = deviceName.replace("\"", "_")
messageData["Data"] = "XBMC|" + machineId + "|" + version + "|" + deviceName
messageString = json.dumps(messageData)
self.logMsg("Opened : " + str(messageString))
ws.send(messageString)
except Exception, e:
self.logMsg("Exception : " + str(e), level=0)
def getWebSocketPort(self, host, port):
userUrl = "http://" + host + ":" + port + "/mediabrowser/System/Info?format=json"
try:
requesthandle = urllib.urlopen(userUrl, proxies={})
jsonData = requesthandle.read()
requesthandle.close()
except Exception, e:
self.logMsg("WebSocketThread getWebSocketPort urlopen : " + str(e) + " (" + userUrl + ")", level=0)
return -1
try:
result = json.loads(jsonData)
except Exception, e:
self.logMsg("WebSocketThread getWebSocketPort jsonload : " + str(e) + " (" + jsonData + ")", level=2)
return -1
wsPort = result.get("WebSocketPortNumber")
if(wsPort != None):
return wsPort
else:
return -1
def run(self):
addonSettings = xbmcaddon.Addon(id='plugin.video.xbmb3c')
mb3Host = addonSettings.getSetting('ipaddress')
mb3Port = addonSettings.getSetting('port')
if(self.logLevel >= 1):
websocket.enableTrace(True)
wsPort = self.getWebSocketPort(mb3Host, mb3Port);
self.logMsg("WebSocketPortNumber = " + str(wsPort))
if(wsPort == -1):
self.logMsg("Could not retrieve WebSocket port, can not run WebScoket Client")
return
# Make a call to /System/Info. WebSocketPortNumber is the port hosting the web socket.
webSocketUrl = "ws://" + mb3Host + ":" + str(wsPort) + "/mediabrowser"
self.logMsg("WebSocket URL : " + webSocketUrl)
self.client = websocket.WebSocketApp(webSocketUrl,
on_message = self.on_message,
on_error = self.on_error,
on_close = self.on_close)
self.client.on_open = self.on_open
while(self.keepRunning):
self.logMsg("Client Starting")
self.client.run_forever()
if(self.keepRunning):
self.logMsg("Client Needs To Restart")
xbmc.sleep(10000)
self.logMsg("Thread Exited")

View File

@ -0,0 +1 @@
# Dummy file to make this directory a package.

902
resources/lib/websocket.py Normal file
View File

@ -0,0 +1,902 @@
"""
websocket - WebSocket client library for Python
Copyright (C) 2010 Hiroki Ohtani(liris)
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
"""
import socket
try:
import ssl
from ssl import SSLError
HAVE_SSL = True
except ImportError:
# dummy class of SSLError for ssl none-support environment.
class SSLError(Exception):
pass
HAVE_SSL = False
from urlparse import urlparse
import os
import array
import struct
import uuid
import hashlib
import base64
import threading
import time
import logging
import traceback
import sys
"""
websocket python client.
=========================
This version support only hybi-13.
Please see http://tools.ietf.org/html/rfc6455 for protocol.
"""
# websocket supported version.
VERSION = 13
# closing frame status codes.
STATUS_NORMAL = 1000
STATUS_GOING_AWAY = 1001
STATUS_PROTOCOL_ERROR = 1002
STATUS_UNSUPPORTED_DATA_TYPE = 1003
STATUS_STATUS_NOT_AVAILABLE = 1005
STATUS_ABNORMAL_CLOSED = 1006
STATUS_INVALID_PAYLOAD = 1007
STATUS_POLICY_VIOLATION = 1008
STATUS_MESSAGE_TOO_BIG = 1009
STATUS_INVALID_EXTENSION = 1010
STATUS_UNEXPECTED_CONDITION = 1011
STATUS_TLS_HANDSHAKE_ERROR = 1015
logger = logging.getLogger()
class WebSocketException(Exception):
"""
websocket exeception class.
"""
pass
class WebSocketConnectionClosedException(WebSocketException):
"""
If remote host closed the connection or some network error happened,
this exception will be raised.
"""
pass
class WebSocketTimeoutException(WebSocketException):
"""
WebSocketTimeoutException will be raised at socket timeout during read/write data.
"""
pass
default_timeout = None
traceEnabled = False
def enableTrace(tracable):
"""
turn on/off the tracability.
tracable: boolean value. if set True, tracability is enabled.
"""
global traceEnabled
traceEnabled = tracable
if tracable:
if not logger.handlers:
logger.addHandler(logging.StreamHandler())
logger.setLevel(logging.DEBUG)
def setdefaulttimeout(timeout):
"""
Set the global timeout setting to connect.
timeout: default socket timeout time. This value is second.
"""
global default_timeout
default_timeout = timeout
def getdefaulttimeout():
"""
Return the global timeout setting(second) to connect.
"""
return default_timeout
def _parse_url(url):
"""
parse url and the result is tuple of
(hostname, port, resource path and the flag of secure mode)
url: url string.
"""
if ":" not in url:
raise ValueError("url is invalid")
scheme, url = url.split(":", 1)
parsed = urlparse(url, scheme="http")
if parsed.hostname:
hostname = parsed.hostname
else:
raise ValueError("hostname is invalid")
port = 0
if parsed.port:
port = parsed.port
is_secure = False
if scheme == "ws":
if not port:
port = 80
elif scheme == "wss":
is_secure = True
if not port:
port = 443
else:
raise ValueError("scheme %s is invalid" % scheme)
if parsed.path:
resource = parsed.path
else:
resource = "/"
if parsed.query:
resource += "?" + parsed.query
return (hostname, port, resource, is_secure)
def create_connection(url, timeout=None, **options):
"""
connect to url and return websocket object.
Connect to url and return the WebSocket object.
Passing optional timeout parameter will set the timeout on the socket.
If no timeout is supplied, the global default timeout setting returned by getdefauttimeout() is used.
You can customize using 'options'.
If you set "header" list object, you can set your own custom header.
>>> conn = create_connection("ws://echo.websocket.org/",
... header=["User-Agent: MyProgram",
... "x-custom: header"])
timeout: socket timeout time. This value is integer.
if you set None for this value, it means "use default_timeout value"
options: current support option is only "header".
if you set header as dict value, the custom HTTP headers are added.
"""
sockopt = options.get("sockopt", [])
sslopt = options.get("sslopt", {})
websock = WebSocket(sockopt=sockopt, sslopt=sslopt)
websock.settimeout(timeout if timeout is not None else default_timeout)
websock.connect(url, **options)
return websock
_MAX_INTEGER = (1 << 32) -1
_AVAILABLE_KEY_CHARS = range(0x21, 0x2f + 1) + range(0x3a, 0x7e + 1)
_MAX_CHAR_BYTE = (1<<8) -1
# ref. Websocket gets an update, and it breaks stuff.
# http://axod.blogspot.com/2010/06/websocket-gets-update-and-it-breaks.html
def _create_sec_websocket_key():
uid = uuid.uuid4()
return base64.encodestring(uid.bytes).strip()
_HEADERS_TO_CHECK = {
"upgrade": "websocket",
"connection": "upgrade",
}
class ABNF(object):
"""
ABNF frame class.
see http://tools.ietf.org/html/rfc5234
and http://tools.ietf.org/html/rfc6455#section-5.2
"""
# operation code values.
OPCODE_CONT = 0x0
OPCODE_TEXT = 0x1
OPCODE_BINARY = 0x2
OPCODE_CLOSE = 0x8
OPCODE_PING = 0x9
OPCODE_PONG = 0xa
# available operation code value tuple
OPCODES = (OPCODE_CONT, OPCODE_TEXT, OPCODE_BINARY, OPCODE_CLOSE,
OPCODE_PING, OPCODE_PONG)
# opcode human readable string
OPCODE_MAP = {
OPCODE_CONT: "cont",
OPCODE_TEXT: "text",
OPCODE_BINARY: "binary",
OPCODE_CLOSE: "close",
OPCODE_PING: "ping",
OPCODE_PONG: "pong"
}
# data length threashold.
LENGTH_7 = 0x7d
LENGTH_16 = 1 << 16
LENGTH_63 = 1 << 63
def __init__(self, fin=0, rsv1=0, rsv2=0, rsv3=0,
opcode=OPCODE_TEXT, mask=1, data=""):
"""
Constructor for ABNF.
please check RFC for arguments.
"""
self.fin = fin
self.rsv1 = rsv1
self.rsv2 = rsv2
self.rsv3 = rsv3
self.opcode = opcode
self.mask = mask
self.data = data
self.get_mask_key = os.urandom
def __str__(self):
return "fin=" + str(self.fin) \
+ " opcode=" + str(self.opcode) \
+ " data=" + str(self.data)
@staticmethod
def create_frame(data, opcode):
"""
create frame to send text, binary and other data.
data: data to send. This is string value(byte array).
if opcode is OPCODE_TEXT and this value is uniocde,
data value is conveted into unicode string, automatically.
opcode: operation code. please see OPCODE_XXX.
"""
if opcode == ABNF.OPCODE_TEXT and isinstance(data, unicode):
data = data.encode("utf-8")
# mask must be set if send data from client
return ABNF(1, 0, 0, 0, opcode, 1, data)
def format(self):
"""
format this object to string(byte array) to send data to server.
"""
if any(x not in (0, 1) for x in [self.fin, self.rsv1, self.rsv2, self.rsv3]):
raise ValueError("not 0 or 1")
if self.opcode not in ABNF.OPCODES:
raise ValueError("Invalid OPCODE")
length = len(self.data)
if length >= ABNF.LENGTH_63:
raise ValueError("data is too long")
frame_header = chr(self.fin << 7
| self.rsv1 << 6 | self.rsv2 << 5 | self.rsv3 << 4
| self.opcode)
if length < ABNF.LENGTH_7:
frame_header += chr(self.mask << 7 | length)
elif length < ABNF.LENGTH_16:
frame_header += chr(self.mask << 7 | 0x7e)
frame_header += struct.pack("!H", length)
else:
frame_header += chr(self.mask << 7 | 0x7f)
frame_header += struct.pack("!Q", length)
if not self.mask:
return frame_header + self.data
else:
mask_key = self.get_mask_key(4)
return frame_header + self._get_masked(mask_key)
def _get_masked(self, mask_key):
s = ABNF.mask(mask_key, self.data)
return mask_key + "".join(s)
@staticmethod
def mask(mask_key, data):
"""
mask or unmask data. Just do xor for each byte
mask_key: 4 byte string(byte).
data: data to mask/unmask.
"""
_m = array.array("B", mask_key)
_d = array.array("B", data)
for i in xrange(len(_d)):
_d[i] ^= _m[i % 4]
return _d.tostring()
class WebSocket(object):
"""
Low level WebSocket interface.
This class is based on
The WebSocket protocol draft-hixie-thewebsocketprotocol-76
http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-76
We can connect to the websocket server and send/recieve data.
The following example is a echo client.
>>> import websocket
>>> ws = websocket.WebSocket()
>>> ws.connect("ws://echo.websocket.org")
>>> ws.send("Hello, Server")
>>> ws.recv()
'Hello, Server'
>>> ws.close()
get_mask_key: a callable to produce new mask keys, see the set_mask_key
function's docstring for more details
sockopt: values for socket.setsockopt.
sockopt must be tuple and each element is argument of sock.setscokopt.
sslopt: dict object for ssl socket option.
"""
def __init__(self, get_mask_key=None, sockopt=None, sslopt=None):
"""
Initalize WebSocket object.
"""
if sockopt is None:
sockopt = []
if sslopt is None:
sslopt = {}
self.connected = False
self.sock = socket.socket()
for opts in sockopt:
self.sock.setsockopt(*opts)
self.sslopt = sslopt
self.get_mask_key = get_mask_key
# Buffers over the packets from the layer beneath until desired amount
# bytes of bytes are received.
self._recv_buffer = []
# These buffer over the build-up of a single frame.
self._frame_header = None
self._frame_length = None
self._frame_mask = None
self._cont_data = None
def fileno(self):
return self.sock.fileno()
def set_mask_key(self, func):
"""
set function to create musk key. You can custumize mask key generator.
Mainly, this is for testing purpose.
func: callable object. the fuct must 1 argument as integer.
The argument means length of mask key.
This func must be return string(byte array),
which length is argument specified.
"""
self.get_mask_key = func
def gettimeout(self):
"""
Get the websocket timeout(second).
"""
return self.sock.gettimeout()
def settimeout(self, timeout):
"""
Set the timeout to the websocket.
timeout: timeout time(second).
"""
self.sock.settimeout(timeout)
timeout = property(gettimeout, settimeout)
def connect(self, url, **options):
"""
Connect to url. url is websocket url scheme. ie. ws://host:port/resource
You can customize using 'options'.
If you set "header" dict object, you can set your own custom header.
>>> ws = WebSocket()
>>> ws.connect("ws://echo.websocket.org/",
... header={"User-Agent: MyProgram",
... "x-custom: header"})
timeout: socket timeout time. This value is integer.
if you set None for this value,
it means "use default_timeout value"
options: current support option is only "header".
if you set header as dict value,
the custom HTTP headers are added.
"""
hostname, port, resource, is_secure = _parse_url(url)
# TODO: we need to support proxy
self.sock.connect((hostname, port))
if is_secure:
if HAVE_SSL:
if self.sslopt is None:
sslopt = {}
else:
sslopt = self.sslopt
self.sock = ssl.wrap_socket(self.sock, **sslopt)
else:
raise WebSocketException("SSL not available.")
self._handshake(hostname, port, resource, **options)
def _handshake(self, host, port, resource, **options):
sock = self.sock
headers = []
headers.append("GET %s HTTP/1.1" % resource)
headers.append("Upgrade: websocket")
headers.append("Connection: Upgrade")
if port == 80:
hostport = host
else:
hostport = "%s:%d" % (host, port)
headers.append("Host: %s" % hostport)
if "origin" in options:
headers.append("Origin: %s" % options["origin"])
else:
headers.append("Origin: http://%s" % hostport)
key = _create_sec_websocket_key()
headers.append("Sec-WebSocket-Key: %s" % key)
headers.append("Sec-WebSocket-Version: %s" % VERSION)
if "header" in options:
headers.extend(options["header"])
headers.append("")
headers.append("")
header_str = "\r\n".join(headers)
self._send(header_str)
if traceEnabled:
logger.debug("--- request header ---")
logger.debug(header_str)
logger.debug("-----------------------")
status, resp_headers = self._read_headers()
if status != 101:
self.close()
raise WebSocketException("Handshake Status %d" % status)
success = self._validate_header(resp_headers, key)
if not success:
self.close()
raise WebSocketException("Invalid WebSocket Header")
self.connected = True
def _validate_header(self, headers, key):
for k, v in _HEADERS_TO_CHECK.iteritems():
r = headers.get(k, None)
if not r:
return False
r = r.lower()
if v != r:
return False
result = headers.get("sec-websocket-accept", None)
if not result:
return False
result = result.lower()
value = key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
hashed = base64.encodestring(hashlib.sha1(value).digest()).strip().lower()
return hashed == result
def _read_headers(self):
status = None
headers = {}
if traceEnabled:
logger.debug("--- response header ---")
while True:
line = self._recv_line()
if line == "\r\n":
break
line = line.strip()
if traceEnabled:
logger.debug(line)
if not status:
status_info = line.split(" ", 2)
status = int(status_info[1])
else:
kv = line.split(":", 1)
if len(kv) == 2:
key, value = kv
headers[key.lower()] = value.strip().lower()
else:
raise WebSocketException("Invalid header")
if traceEnabled:
logger.debug("-----------------------")
return status, headers
def send(self, payload, opcode=ABNF.OPCODE_TEXT):
"""
Send the data as string.
payload: Payload must be utf-8 string or unicoce,
if the opcode is OPCODE_TEXT.
Otherwise, it must be string(byte array)
opcode: operation code to send. Please see OPCODE_XXX.
"""
frame = ABNF.create_frame(payload, opcode)
if self.get_mask_key:
frame.get_mask_key = self.get_mask_key
data = frame.format()
length = len(data)
if traceEnabled:
logger.debug("send: " + repr(data))
while data:
l = self._send(data)
data = data[l:]
return length
def send_binary(self, payload):
return self.send(payload, ABNF.OPCODE_BINARY)
def ping(self, payload=""):
"""
send ping data.
payload: data payload to send server.
"""
self.send(payload, ABNF.OPCODE_PING)
def pong(self, payload):
"""
send pong data.
payload: data payload to send server.
"""
self.send(payload, ABNF.OPCODE_PONG)
def recv(self):
"""
Receive string data(byte array) from the server.
return value: string(byte array) value.
"""
opcode, data = self.recv_data()
return data
def recv_data(self):
"""
Recieve data with operation code.
return value: tuple of operation code and string(byte array) value.
"""
while True:
frame = self.recv_frame()
if not frame:
# handle error:
# 'NoneType' object has no attribute 'opcode'
raise WebSocketException("Not a valid frame %s" % frame)
elif frame.opcode in (ABNF.OPCODE_TEXT, ABNF.OPCODE_BINARY, ABNF.OPCODE_CONT):
if frame.opcode == ABNF.OPCODE_CONT and not self._cont_data:
raise WebSocketException("Illegal frame")
if self._cont_data:
self._cont_data[1] += frame.data
else:
self._cont_data = [frame.opcode, frame.data]
if frame.fin:
data = self._cont_data
self._cont_data = None
return data
elif frame.opcode == ABNF.OPCODE_CLOSE:
self.send_close()
return (frame.opcode, None)
elif frame.opcode == ABNF.OPCODE_PING:
self.pong(frame.data)
def recv_frame(self):
"""
recieve data as frame from server.
return value: ABNF frame object.
"""
# Header
if self._frame_header is None:
self._frame_header = self._recv_strict(2)
b1 = ord(self._frame_header[0])
fin = b1 >> 7 & 1
rsv1 = b1 >> 6 & 1
rsv2 = b1 >> 5 & 1
rsv3 = b1 >> 4 & 1
opcode = b1 & 0xf
b2 = ord(self._frame_header[1])
has_mask = b2 >> 7 & 1
# Frame length
if self._frame_length is None:
length_bits = b2 & 0x7f
if length_bits == 0x7e:
length_data = self._recv_strict(2)
self._frame_length = struct.unpack("!H", length_data)[0]
elif length_bits == 0x7f:
length_data = self._recv_strict(8)
self._frame_length = struct.unpack("!Q", length_data)[0]
else:
self._frame_length = length_bits
# Mask
if self._frame_mask is None:
self._frame_mask = self._recv_strict(4) if has_mask else ""
# Payload
payload = self._recv_strict(self._frame_length)
if has_mask:
payload = ABNF.mask(self._frame_mask, payload)
# Reset for next frame
self._frame_header = None
self._frame_length = None
self._frame_mask = None
return ABNF(fin, rsv1, rsv2, rsv3, opcode, has_mask, payload)
def send_close(self, status=STATUS_NORMAL, reason=""):
"""
send close data to the server.
status: status code to send. see STATUS_XXX.
reason: the reason to close. This must be string.
"""
if status < 0 or status >= ABNF.LENGTH_16:
raise ValueError("code is invalid range")
self.send(struct.pack('!H', status) + reason, ABNF.OPCODE_CLOSE)
def close(self, status=STATUS_NORMAL, reason=""):
"""
Close Websocket object
status: status code to send. see STATUS_XXX.
reason: the reason to close. This must be string.
"""
if self.connected:
if status < 0 or status >= ABNF.LENGTH_16:
raise ValueError("code is invalid range")
try:
self.send(struct.pack('!H', status) + reason, ABNF.OPCODE_CLOSE)
timeout = self.sock.gettimeout()
self.sock.settimeout(3)
try:
frame = self.recv_frame()
if logger.isEnabledFor(logging.ERROR):
recv_status = struct.unpack("!H", frame.data)[0]
if recv_status != STATUS_NORMAL:
logger.error("close status: " + repr(recv_status))
except:
pass
self.sock.settimeout(timeout)
self.sock.shutdown(socket.SHUT_RDWR)
except:
pass
self._closeInternal()
def _closeInternal(self):
self.connected = False
self.sock.close()
def _send(self, data):
try:
return self.sock.send(data)
except socket.timeout as e:
raise WebSocketTimeoutException(e.args[0])
except Exception as e:
if "timed out" in e.args[0]:
raise WebSocketTimeoutException(e.args[0])
else:
raise e
def _recv(self, bufsize):
try:
bytes = self.sock.recv(bufsize)
except socket.timeout as e:
raise WebSocketTimeoutException(e.args[0])
except SSLError as e:
if e.args[0] == "The read operation timed out":
raise WebSocketTimeoutException(e.args[0])
else:
raise
if not bytes:
raise WebSocketConnectionClosedException()
return bytes
def _recv_strict(self, bufsize):
shortage = bufsize - sum(len(x) for x in self._recv_buffer)
while shortage > 0:
bytes = self._recv(shortage)
self._recv_buffer.append(bytes)
shortage -= len(bytes)
unified = "".join(self._recv_buffer)
if shortage == 0:
self._recv_buffer = []
return unified
else:
self._recv_buffer = [unified[bufsize:]]
return unified[:bufsize]
def _recv_line(self):
line = []
while True:
c = self._recv(1)
line.append(c)
if c == "\n":
break
return "".join(line)
class WebSocketApp(object):
"""
Higher level of APIs are provided.
The interface is like JavaScript WebSocket object.
"""
def __init__(self, url, header=[],
on_open=None, on_message=None, on_error=None,
on_close=None, keep_running=True, get_mask_key=None):
"""
url: websocket url.
header: custom header for websocket handshake.
on_open: callable object which is called at opening websocket.
this function has one argument. The arugment is this class object.
on_message: callbale object which is called when recieved data.
on_message has 2 arguments.
The 1st arugment is this class object.
The passing 2nd arugment is utf-8 string which we get from the server.
on_error: callable object which is called when we get error.
on_error has 2 arguments.
The 1st arugment is this class object.
The passing 2nd arugment is exception object.
on_close: callable object which is called when closed the connection.
this function has one argument. The arugment is this class object.
keep_running: a boolean flag indicating whether the app's main loop should
keep running, defaults to True
get_mask_key: a callable to produce new mask keys, see the WebSocket.set_mask_key's
docstring for more information
"""
self.url = url
self.header = header
self.on_open = on_open
self.on_message = on_message
self.on_error = on_error
self.on_close = on_close
self.keep_running = keep_running
self.get_mask_key = get_mask_key
self.sock = None
def send(self, data, opcode=ABNF.OPCODE_TEXT):
"""
send message.
data: message to send. If you set opcode to OPCODE_TEXT, data must be utf-8 string or unicode.
opcode: operation code of data. default is OPCODE_TEXT.
"""
if self.sock.send(data, opcode) == 0:
raise WebSocketConnectionClosedException()
def close(self):
"""
close websocket connection.
"""
self.keep_running = False
self.sock.close()
def _send_ping(self, interval):
while True:
for i in range(interval):
time.sleep(1)
if not self.keep_running:
return
self.sock.ping()
def run_forever(self, sockopt=None, sslopt=None, ping_interval=0):
"""
run event loop for WebSocket framework.
This loop is infinite loop and is alive during websocket is available.
sockopt: values for socket.setsockopt.
sockopt must be tuple and each element is argument of sock.setscokopt.
sslopt: ssl socket optional dict.
ping_interval: automatically send "ping" command every specified period(second)
if set to 0, not send automatically.
"""
if sockopt is None:
sockopt = []
if sslopt is None:
sslopt = {}
if self.sock:
raise WebSocketException("socket is already opened")
thread = None
try:
self.sock = WebSocket(self.get_mask_key, sockopt=sockopt, sslopt=sslopt)
self.sock.settimeout(2)#default_timeout)
self.sock.connect(self.url, header=self.header)
self._callback(self.on_open)
if ping_interval:
thread = threading.Thread(target=self._send_ping, args=(ping_interval,))
thread.setDaemon(True)
thread.start()
while self.keep_running:
try:
data = self.sock.recv()
if data is None or self.keep_running == False:
break
self._callback(self.on_message, data)
except Exception, e:
#print str(e.args[0])
if "timed out" not in e.args[0]:
raise e
except Exception, e:
self._callback(self.on_error, e)
finally:
if thread:
self.keep_running = False
self.sock.close()
self._callback(self.on_close)
self.sock = None
def _callback(self, callback, *args):
if callback:
try:
callback(self, *args)
except Exception, e:
logger.error(e)
if logger.isEnabledFor(logging.DEBUG):
_, _, tb = sys.exc_info()
traceback.print_tb(tb)
if __name__ == "__main__":
enableTrace(True)
ws = create_connection("ws://echo.websocket.org/")
print("Sending 'Hello, World'...")
ws.send("Hello, World")
print("Sent")
print("Receiving...")
result = ws.recv()
print("Received '%s'" % result)
ws.close()

BIN
resources/mb3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 117 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

66
resources/settings.xml Normal file
View File

@ -0,0 +1,66 @@
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<settings>
<category label="30014"> <!-- MediaBrowser -->
<setting id="ipaddress" type="text" label="30000" default="&lt;none&gt;" visible="true" enable="true" />
<setting id="port" type="text" label="30030" default="8096" visible="true" enable="true" />
<setting type="sep" />
<setting id="username" type="text" label="30024" />
<setting id="password" type="text" option="hidden" label="30025" />
</category>
<category label="30015"> <!-- Network -->
<setting id="smbusername" type="text" label="30007" default="" visible="true" enable="true" />
<setting id="smbpassword" type="text" label="30008" default="" option="hidden" visible="true" enable="true" />
<setting id="videoBitRate" type="enum" label="30160" values="664 Kbps SD|996 Kbps HD|1.3 Mbps HD|2.0 Mbps HD|3.2 Mbps HD|4.7 Mbps HD|6.2 Mbps HD|7.7 Mbps HD|9.2 Mbps HD|10.7 Mbps HD|12.2 Mbps HD|13.7 Mbps HD|15.2 Mbps HD|16.7 Mbps HD|18.2 Mbps HD|20.0 Mbps HD|40.0 Mbps HD|100.0 Mbps HD [default]|1000.0 Mbps HD" default="17" />
<setting id="deviceName" type="text" label="30016" default="XBMB3C" visible="true" enable="true" />
<setting id="playFromStream" type="bool" label="30002" default="false" visible="true" enable="true" />
</category>
<category label="30110"> <!-- Interface -->
<setting id="selectAction" type="enum" label="30151" values="Play|Info" default="0" />
<setting id="resumeJumpBack" type="number" label="30114" default="10" visible="true" enable="true" />
<setting id="markPlayedAt" type="number" label="30115" default="90" visible="true" enable="true" />
<setting id="addCounts" type="bool" label="30116" default="true" visible="true" enable="true" />
<setting id="addSeasonNumber" type="bool" label="30162" default="false" visible="true" enable="true" />
<setting id="addEpisodeNumber" type="bool" label="30119" default="true" visible="true" enable="true" />
<setting id="addResumePercent" type="bool" label="30118" default="true" visible="true" enable="true" />
<setting id="flattenSeasons" type="bool" label="30163" default="false" visible="true" enable="true" />
<setting id="autoEnterSingle" type="bool" label="30001" default="true" visible="true" enable="true" />
<setting id="sortNextUp" type="bool" label="30156" default="false" visible="true" enable="true" />
<setting id="offerDelete" type="bool" label="30127" default="false" visible="true" enable="true" />
<setting id="showLoadProgress" type="bool" label="30120" default="false" visible="true" enable="true" />
</category>
<category label="30158"> <!-- Metadata -->
<setting id="includeStreamInfo" type="bool" label="30111" default="true" visible="true" enable="true" />
<setting id="includePeople" type="bool" label="30112" default="true" visible="true" enable="true" />
<setting id="includeOverview" type="bool" label="30113" default="true" visible="true" enable="true" />
<setting id="numRecentMovies" type="number" label="30036" default="20" visible="true" enable="true" />
<setting id="numRecentTV" type="number" label="30037" default="20" visible="true" enable="true" />
<setting id="numRecentMusic" type="number" label="30035" default="20" visible="true" enable="true" />
</category>
<category label="30159"> <!-- Artwork -->
<setting id="backgroundRefresh" type="number" label="30117" default="30" visible="true" enable="true" />
<setting id="useSeasonPoster" type="bool" label="30039" default="false" visible="true" enable="true" />
<setting id="disableCoverArt" type="bool" label="30157" default="false" visible="true" enable="true" />
<setting id="showIndicators" type="bool" label="30152" default="false" visible="true" enable="true" />
<setting id="showWatchedIndicators" type="bool" label="30153" default="true" visible="eq(-1,true)" enable="eq(-1,true)" />
<setting id="showUnplayedIndicators" type="bool" label="30154" default="true" visible="eq(-2,true)" enable="eq(-2,true)" />
<setting id="showPlayedPrecentageIndicators" type="bool" label="30155" default="true" visible="eq(-3,true)" enable="eq(-3,true)" />
<setting id="useThemeMusic" type="bool" label="30139" default="true" visible="true" enable="true" />
<setting id="loopThemeMusic" type="bool" label="30140" default="true" visible="true" enable="true" />
</category>
<category label="30142"> <!-- Services -->
<setting id="useBackgroundLoader" type="bool" label="30141" default="true" visible="true" enable="true" />
<setting id="useInfoLoader" type="bool" label="30143" default="true" visible="true" enable="true" />
<setting id="useMenuLoader" type="bool" label="30144" default="true" visible="true" enable="true" />
<setting id="useWebSocketRemote" type="bool" label="30145" default="true" visible="true" enable="true" />
<setting id="useInProgressUpdater" type="bool" label="30146" default="true" visible="true" enable="true" />
<setting id="useRecentInfoUpdater" type="bool" label="30147" default="true" visible="true" enable="true" />
<setting id="useRandomInfo" type="bool" label="30148" default="true" visible="true" enable="true" />
<setting id="useNextUp" type="bool" label="30149" default="true" visible="true" enable="true" />
<setting id="useSuggested" type="bool" label="30161" default="true" visible="true" enable="true" />
</category>
<category label="30022"> <!-- Advanced -->
<setting id="logLevel" type="enum" label="30004" values="None(0)|Info(1)|Debug(2)" default="0" />
<setting id="profile" type="bool" label="30010" default="false" visible="true" enable="true" />
<!--setting id="useJson" type="bool" label="30026" default="false" visible="true" enable="true" /> -->
</category>
</settings>

View File

@ -0,0 +1,415 @@
<?xml version="1.0" encoding="UTF-8"?>
<window id="3300" type="dialog">
<defaultcontrol always="true">3002</defaultcontrol>
<zorder>2</zorder>
<coordinates>
<system>1</system>
<left>120</left>
<top>50</top>
</coordinates>
<include>dialogeffect</include>
<controls>
<control type="image">
<left>0</left>
<top>0</top>
<width>1040</width>
<height>600</height>
<texture border="40">DialogBack.png</texture>
</control>
<control type="image" id="3001">
<left>20</left>
<top>20</top>
<width>1000</width>
<height>560</height>
<colordiffuse>FF444444</colordiffuse>
</control>
<control type="label" id="3000">
<left>30</left>
<top>25</top>
<width>950</width>
<height>20</height>
<align>left</align>
<label>-</label>
<font>font24_title</font>
<textcolor>FFFFFFFFFF</textcolor>
</control>
<control type="label" id="3003">
<left>30</left>
<top>55</top>
<width>300</width>
<height>20</height>
<align>left</align>
<label>-</label>
<font>font18_title</font>
<textcolor>FFFFFFFFFF</textcolor>
</control>
<!-- episode image 16x9 -->
<control type="image" id="3009">
<left>40</left>
<top>130</top>
<width>250</width>
<height>140</height>
<aspectratio>stretch</aspectratio>
</control>
<control type="image" id="3010">
<left>40</left>
<top>265</top>
<width>250</width>
<height>5</height>
<texture background="true">-</texture>
<colordiffuse>AAFFFFFF</colordiffuse>
<aspectratio>stretch</aspectratio>
</control>
<!-- poster image -->
<control type="image" id="3011">
<left>60</left>
<top>100</top>
<width>175</width>
<height>250</height>
<aspectratio>stretch</aspectratio>
</control>
<control type="image" id="3012">
<left>60</left>
<top>345</top>
<width>175</width>
<height>5</height>
<texture background="true">-</texture>
<colordiffuse>AAFFFFFF</colordiffuse>
<aspectratio>stretch</aspectratio>
</control>
<control type="list" id="3220">
<left>30</left>
<top>380</top>
<width>240</width>
<height>120</height>
<onleft>3002</onleft>
<onright>3221</onright>
<onup>3235</onup>
<ondown>3002</ondown>
<pagecontrol>3221</pagecontrol>
<scrolltime>200</scrolltime>
<itemlayout height="20">
<control type="label">
<left>60</left>
<top>0</top>
<width>60</width>
<height>20</height>
<font>font10</font>
<align>right</align>
<aligny>center</aligny>
<textcolor>blue</textcolor>
<selectedcolor>selected</selectedcolor>
<info>ListItem.Label</info>
</control>
<control type="label">
<left>65</left>
<top>0</top>
<width>180</width>
<height>20</height>
<font>font10</font>
<align>left</align>
<aligny>center</aligny>
<textcolor>white</textcolor>
<selectedcolor>white</selectedcolor>
<info>ListItem.Label2</info>
</control>
</itemlayout>
<focusedlayout height="20">
<control type="image">
<left>0</left>
<top>0</top>
<width>240</width>
<height>20</height>
<visible>Control.HasFocus(3220)</visible>
<texture>MenuItemFO.png</texture>
<include>VisibleFadeEffect</include>
</control>
<control type="label">
<left>60</left>
<top>0</top>
<width>60</width>
<height>20</height>
<font>font10</font>
<align>right</align>
<aligny>center</aligny>
<textcolor>blue</textcolor>
<selectedcolor>selected</selectedcolor>
<info>ListItem.Label</info>
</control>
<control type="label">
<left>65</left>
<top>0</top>
<width>180</width>
<height>20</height>
<font>font10</font>
<align>left</align>
<aligny>center</aligny>
<textcolor>white</textcolor>
<selectedcolor>white</selectedcolor>
<info>ListItem.Label2</info>
</control>
</focusedlayout>
</control>
<control type="scrollbar" id="3221">
<left>270</left>
<top>380</top>
<width>20</width>
<height>120</height>
<texturesliderbackground border="0,14,0,14">ScrollBarV.png</texturesliderbackground>
<texturesliderbar border="2,16,2,16">ScrollBarV_bar.png</texturesliderbar>
<texturesliderbarfocus border="2,16,2,16">ScrollBarV_bar_focus.png</texturesliderbarfocus>
<textureslidernib>ScrollBarNib.png</textureslidernib>
<textureslidernibfocus>ScrollBarNib.png</textureslidernibfocus>
<onleft>3220</onleft>
<onright>3226</onright>
<showonepage>false</showonepage>
<orientation>vertical</orientation>
</control>
<control type="list" id="3226">
<left>310</left>
<top>380</top>
<width>415</width>
<height>120</height>
<onleft>3221</onleft>
<onright>3235</onright>
<onup>3235</onup>
<ondown>3002</ondown>
<pagecontrol>-</pagecontrol>
<scrolltime>200</scrolltime>
<itemlayout height="20">
<control type="label">
<left>70</left>
<top>0</top>
<width>70</width>
<height>20</height>
<font>font10</font>
<align>right</align>
<aligny>center</aligny>
<textcolor>blue</textcolor>
<selectedcolor>selected</selectedcolor>
<info>ListItem.Label</info>
</control>
<control type="label">
<left>75</left>
<top>0</top>
<width>340</width>
<height>20</height>
<font>font10</font>
<align>left</align>
<aligny>center</aligny>
<textcolor>white</textcolor>
<selectedcolor>white</selectedcolor>
<info>ListItem.Label2</info>
</control>
</itemlayout>
<focusedlayout height="20">
<control type="image">
<left>0</left>
<top>0</top>
<width>400</width>
<height>20</height>
<visible>Control.HasFocus(3226)</visible>
<texture>MenuItemFO.png</texture>
<include>VisibleFadeEffect</include>
</control>
<control type="label">
<left>70</left>
<top>0</top>
<width>70</width>
<height>20</height>
<font>font10</font>
<align>right</align>
<aligny>center</aligny>
<textcolor>blue</textcolor>
<selectedcolor>selected</selectedcolor>
<info>ListItem.Label</info>
</control>
<control type="label">
<left>75</left>
<top>0</top>
<width>340</width>
<height>20</height>
<font>font10</font>
<align>left</align>
<aligny>center</aligny>
<textcolor>white</textcolor>
<selectedcolor>white</selectedcolor>
<info>ListItem.Label2</info>
</control>
</focusedlayout>
</control>
<!--
<control type="scrollbar" id="3227">
<left>270</left>
<top>380</top>
<width>20</width>
<height>120</height>
<texturesliderbackground border="0,14,0,14">ScrollBarV.png</texturesliderbackground>
<texturesliderbar border="2,16,2,16">ScrollBarV_bar.png</texturesliderbar>
<texturesliderbarfocus border="2,16,2,16">ScrollBarV_bar_focus.png</texturesliderbarfocus>
<textureslidernib>ScrollBarNib.png</textureslidernib>
<textureslidernibfocus>ScrollBarNib.png</textureslidernibfocus>
<onleft>3220</onleft>
<onright>3235</onright>
<showonepage>false</showonepage>
<orientation>vertical</orientation>
</control>
-->
<control type="textbox" id="3223">
<left>320</left>
<top>100</top>
<width>400</width>
<height>250</height>
<font>font12</font>
<!--<align>justify</align>-->
<textcolor>white</textcolor>
<pagecontrol>3235</pagecontrol>
<visible>true</visible>
</control>
<control type="scrollbar" id="3235">
<left>720</left>
<top>100</top>
<width>20</width>
<height>250</height>
<texturesliderbackground border="0,14,0,14">ScrollBarV.png</texturesliderbackground>
<texturesliderbar border="2,16,2,16">ScrollBarV_bar.png</texturesliderbar>
<texturesliderbarfocus border="2,16,2,16">ScrollBarV_bar_focus.png</texturesliderbarfocus>
<textureslidernib>ScrollBarNib.png</textureslidernib>
<textureslidernibfocus>ScrollBarNib.png</textureslidernibfocus>
<onleft>3226</onleft>
<onup>-</onup>
<onright>3230</onright>
<showonepage>false</showonepage>
<orientation>vertical</orientation>
</control>
<control type="list" id="3230">
<left>760</left>
<top>100</top>
<width>245calc</width>
<height>450</height>
<onleft>3235</onleft>
<onright>3231</onright>
<onup>-</onup>
<ondown>-</ondown>
<pagecontrol>3231</pagecontrol>
<scrolltime>200</scrolltime>
<itemlayout height="60">
<control type="image">
<left>0</left>
<top>0</top>
<width>60</width>
<height>60</height>
<texture fallback="DefaultArtist.png">$INFO[Listitem.Icon]</texture>
<aspectratio>scale</aspectratio>
</control>
<control type="label">
<left>65</left>
<top>0</top>
<width>160</width>
<height>30</height>
<font>font12</font>
<align>left</align>
<aligny>center</aligny>
<textcolor>blue</textcolor>
<selectedcolor>selected</selectedcolor>
<info>ListItem.Label</info>
</control>
<control type="label">
<left>65</left>
<top>30</top>
<width>160</width>
<height>30</height>
<font>font10</font>
<align>left</align>
<aligny>center</aligny>
<textcolor>white</textcolor>
<selectedcolor>white</selectedcolor>
<info>ListItem.Label2</info>
</control>
</itemlayout>
<focusedlayout height="60">
<control type="image">
<left>0</left>
<top>0</top>
<width>60</width>
<height>60</height>
<texture fallback="DefaultArtist.png">$INFO[Listitem.Icon]</texture>
<aspectratio>scale</aspectratio>
</control>
<control type="image">
<left>60</left>
<top>0</top>
<width>160</width>
<height>30</height>
<visible>Control.HasFocus(3230)</visible>
<texture>MenuItemFO.png</texture>
<include>VisibleFadeEffect</include>
</control>
<control type="label">
<left>65</left>
<top>0</top>
<width>160</width>
<height>30</height>
<font>font12</font>
<align>left</align>
<aligny>center</aligny>
<textcolor>blue</textcolor>
<selectedcolor>selected</selectedcolor>
<info>ListItem.Label</info>
</control>
<control type="label">
<left>65</left>
<top>30</top>
<width>160</width>
<height>30</height>
<font>font10</font>
<align>left</align>
<aligny>center</aligny>
<textcolor>white</textcolor>
<selectedcolor>white</selectedcolor>
<info>ListItem.Label2</info>
</control>
</focusedlayout>
</control>
<control type="scrollbar" id="3231">
<left>985</left>
<top>100</top>
<width>20</width>
<height>450</height>
<texturesliderbackground border="0,14,0,14">ScrollBarV.png</texturesliderbackground>
<texturesliderbar border="2,16,2,16">ScrollBarV_bar.png</texturesliderbar>
<texturesliderbarfocus border="2,16,2,16">ScrollBarV_bar_focus.png</texturesliderbarfocus>
<textureslidernib>ScrollBarNib.png</textureslidernib>
<textureslidernibfocus>ScrollBarNib.png</textureslidernibfocus>
<onleft>3230</onleft>
<onright>-</onright>
<showonepage>false</showonepage>
<orientation>vertical</orientation>
</control>
<control type="button" id="3002">
<left>30</left>
<top>520</top>
<width>150</width>
<height>40</height>
<align>center</align>
<label>Play</label>
<font>font13</font>
<onleft>-</onleft>
<onright>3220</onright>
<onup>3220</onup>
</control>
</controls>
</window>

View File

@ -0,0 +1,205 @@
<?xml version="1.0" encoding="UTF-8"?>
<window>
<defaultcontrol always="true">3010</defaultcontrol>
<zorder>2</zorder>
<coordinates>
<system>1</system>
<left>120</left>
<top>50</top>
</coordinates>
<include>dialogeffect</include>
<controls>
<control type="image">
<left>0</left>
<top>0</top>
<width>1040</width>
<height>600</height>
<texture border="40">DialogBack.png</texture>
</control>
<control type="image">
<left>20</left>
<top>20</top>
<width>1000</width>
<height>560</height>
<texture>$INFO[Skin.CurrentTheme,special://skin/backgrounds/,.jpg]</texture>
<visible>![Skin.HasSetting(UseCustomBackground) + !IsEmpty(Skin.String(CustomBackgroundPath))]</visible>
<include>VisibleFadeEffect</include>
<colordiffuse>FF444444</colordiffuse>
</control>
<control type="image">
<description>Dialog Header image</description>
<left>40</left>
<top>16</top>
<width>960</width>
<height>40</height>
<texture>dialogheader.png</texture>
</control>
<control type="label" id="1">
<description>header label</description>
<left>40</left>
<top>20</top>
<width>960</width>
<height>30</height>
<font>font13_title</font>
<label>Person Info</label>
<align>center</align>
<aligny>center</aligny>
<textcolor>selected</textcolor>
<shadowcolor>black</shadowcolor>
</control>
<!--
<control type="button" id="8">
<description>Close Window button</description>
<left>960</left>
<top>15</top>
<width>64</width>
<height>32</height>
<label>-</label>
<font>-</font>
<onclick>PreviousMenu</onclick>
<texturefocus>DialogCloseButton-focus.png</texturefocus>
<texturenofocus>DialogCloseButton.png</texturenofocus>
<onleft>-</onleft>
<onright>-</onright>
<onup>-</onup>
<ondown>3005</ondown>
</control>
-->
<control type="label" id="3000">
<description>person name</description>
<left>30</left>
<top>65</top>
<width>550</width>
<height>100</height>
<align>left</align>
<label>-</label>
<font>font13</font>
<textcolor>white</textcolor>
</control>
<control type="image" id="3009">
<left>30</left>
<top>120</top>
<width>250</width>
<height>250</height>
<aspectratio>keep</aspectratio>
</control>
<control type="textbox" id="3001">
<description>text</description>
<left>300</left>
<top>100</top>
<width>630</width>
<height>280</height>
<align>left</align>
<label>-</label>
<font>font12</font>
<pagecontrol>3005</pagecontrol>
</control>
<control type="scrollbar" id="3005">
<left>940</left>
<top>100</top>
<width>20</width>
<height>280</height>
<texturesliderbackground border="0,14,0,14">ScrollBarV.png</texturesliderbackground>
<texturesliderbar border="2,16,2,16">ScrollBarV_bar.png</texturesliderbar>
<texturesliderbarfocus border="2,16,2,16">ScrollBarV_bar_focus.png</texturesliderbarfocus>
<textureslidernib>ScrollBarNib.png</textureslidernib>
<textureslidernibfocus>ScrollBarNib.png</textureslidernibfocus>
<onup>8</onup>
<onleft>3001</onleft>
<onright>-</onright>
<ondown>3010</ondown>
<showonepage>false</showonepage>
<orientation>vertical</orientation>
</control>
<control type="list" id="3010">
<left>40</left>
<top>390</top>
<width>940</width>
<height>170</height>
<onleft>-</onleft>
<onright>-</onright>
<onup>3005</onup>
<ondown>3011</ondown>
<pagecontrol>3011</pagecontrol>
<scrolltime>200</scrolltime>
<orientation>horizontal</orientation>
<itemlayout width="120">
<control type="image">
<left>0</left>
<top>0</top>
<width>100</width>
<height>150</height>
<texture>$INFO[Listitem.Icon]</texture>
<bordertexture border="5">button-nofocus.png</bordertexture>
<bordersize>5</bordersize>
</control>
<control type="label">
<left>0</left>
<top>150</top>
<width>100</width>
<height>20</height>
<align>left</align>
<font>font10</font>
<textcolor>FFFFFFFFFF</textcolor>
<label>$INFO[Listitem.Label2]</label>
</control>
</itemlayout>
<focusedlayout width="120">
<control type="image">
<left>0</left>
<top>0</top>
<width>100</width>
<height>150</height>
<texture>$INFO[Listitem.Icon]</texture>
<bordertexture border="5">button-nofocus.png</bordertexture>
<bordersize>5</bordersize>
</control>
<control type="image">
<left>0</left>
<top>0</top>
<width>100</width>
<height>150</height>
<texture>$INFO[Listitem.Icon]</texture>
<bordertexture border="5">button-focus.png</bordertexture>
<bordersize>5</bordersize>
<visible>Control.HasFocus(3010)</visible>
</control>
<control type="label">
<left>0</left>
<top>150</top>
<width>100</width>
<height>20</height>
<align>left</align>
<font>font10</font>
<textcolor>FFFFFFFFFF</textcolor>
<label>$INFO[Listitem.Label2]</label>
</control>
</focusedlayout>
</control>
<control type="scrollbar" id="3011">
<left>40</left>
<top>560</top>
<width>940</width>
<height>20</height>
<texturesliderbackground border="14,0,14,0">ScrollBarH.png</texturesliderbackground>
<texturesliderbar border="16,2,16,2">ScrollBarH_bar.png</texturesliderbar>
<texturesliderbarfocus border="16,2,16,2">ScrollBarH_bar_focus.png</texturesliderbarfocus>
<textureslidernib>ScrollBarNib.png</textureslidernib>
<textureslidernibfocus>ScrollBarNib.png</textureslidernibfocus>
<onup>3010</onup>
<showonepage>false</showonepage>
<orientation>horizontal</orientation>
</control>
</controls>
</window>

View File

@ -0,0 +1,910 @@
<?xml version="1.0" encoding="UTF-8"?>
<window>
<defaultcontrol always="true">3020</defaultcontrol>
<zorder>2</zorder>
<coordinates>
<system>1</system>
<left>120</left>
<top>50</top>
</coordinates>
<!--<include>dialogeffect</include>-->
<controls>
<control type="image">
<left>0</left>
<top>0</top>
<width>1040</width>
<height>600</height>
<texture border="40">DialogBack.png</texture>
</control>
<control type="image">
<left>20</left>
<top>20</top>
<width>1000</width>
<height>560</height>
<texture>$INFO[Skin.CurrentTheme,special://skin/backgrounds/,.jpg]</texture>
<visible>![Skin.HasSetting(UseCustomBackground) + !IsEmpty(Skin.String(CustomBackgroundPath))]</visible>
<include>VisibleFadeEffect</include>
<colordiffuse>FF444444</colordiffuse>
</control>
<control type="image">
<left>25</left>
<top>98</top>
<width>190</width>
<height>30</height>
<aspectratio>stretch</aspectratio>
<texture border="20">KeyboardEditArea.png</texture>
</control>
<control type="label" id="3010">
<left>30</left>
<top>100</top>
<width>180</width>
<height>40</height>
<font>font13</font>
<onleft>-</onleft>
<onright>-</onright>
<onup>-</onup>
<ondown>-</ondown>
</control>
<control type="group">
<left>30</left>
<top>140</top>
<!-- First Row -->
<control type="button" id="3020">
<left>0</left>
<top>0</top>
<width>30</width>
<height>30</height>
<label>A</label>
<onleft>-</onleft>
<onright>3021</onright>
<onup>-</onup>
<ondown>3026</ondown>
<align>center</align>
<aligny>center</aligny>
<font>font12</font>
</control>
<control type="button" id="3021">
<left>30</left>
<top>0</top>
<width>30</width>
<height>30</height>
<label>B</label>
<onleft>3020</onleft>
<onright>3022</onright>
<onup>-</onup>
<ondown>3027</ondown>
<align>center</align>
<aligny>center</aligny>
<font>font12</font>
</control>
<control type="button" id="3022">
<left>60</left>
<top>0</top>
<width>30</width>
<height>30</height>
<label>C</label>
<onleft>3021</onleft>
<onright>3023</onright>
<onup>-</onup>
<ondown>3028</ondown>
<align>center</align>
<aligny>center</aligny>
<font>font12</font>
</control>
<control type="button" id="3023">
<left>90</left>
<top>0</top>
<width>30</width>
<height>30</height>
<label>D</label>
<onleft>3022</onleft>
<onright>3024</onright>
<onup>-</onup>
<ondown>3029</ondown>
<align>center</align>
<aligny>center</aligny>
<font>font12</font>
</control>
<control type="button" id="3024">
<left>120</left>
<top>0</top>
<width>30</width>
<height>30</height>
<label>E</label>
<onleft>3023</onleft>
<onright>3025</onright>
<onup>-</onup>
<ondown>3030</ondown>
<align>center</align>
<aligny>center</aligny>
<font>font12</font>
</control>
<control type="button" id="3025">
<left>150</left>
<top>0</top>
<width>30</width>
<height>30</height>
<label>F</label>
<onleft>3024</onleft>
<onright>3110</onright>
<onup>-</onup>
<ondown>3031</ondown>
<align>center</align>
<aligny>center</aligny>
<font>font12</font>
</control>
<!-- Second Row -->
<control type="button" id="3026">
<left>0</left>
<top>30</top>
<width>30</width>
<height>30</height>
<label>G</label>
<onleft>-</onleft>
<onright>3027</onright>
<onup>3020</onup>
<ondown>3032</ondown>
<align>center</align>
<aligny>center</aligny>
<font>font12</font>
</control>
<control type="button" id="3027">
<left>30</left>
<top>30</top>
<width>30</width>
<height>30</height>
<label>H</label>
<onleft>3026</onleft>
<onright>3028</onright>
<onup>3021</onup>
<ondown>3033</ondown>
<align>center</align>
<aligny>center</aligny>
<font>font12</font>
</control>
<control type="button" id="3028">
<left>60</left>
<top>30</top>
<width>30</width>
<height>30</height>
<label>I</label>
<onleft>3027</onleft>
<onright>3029</onright>
<onup>3022</onup>
<ondown>3034</ondown>
<align>center</align>
<aligny>center</aligny>
<font>font12</font>
</control>
<control type="button" id="3029">
<left>90</left>
<top>30</top>
<width>30</width>
<height>30</height>
<label>J</label>
<onleft>3028</onleft>
<onright>3030</onright>
<onup>3023</onup>
<ondown>3035</ondown>
<align>center</align>
<aligny>center</aligny>
<font>font12</font>
</control>
<control type="button" id="3030">
<left>120</left>
<top>30</top>
<width>30</width>
<height>30</height>
<label>K</label>
<onleft>3029</onleft>
<onright>3031</onright>
<onup>3024</onup>
<ondown>3036</ondown>
<align>center</align>
<aligny>center</aligny>
<font>font12</font>
</control>
<control type="button" id="3031">
<left>150</left>
<top>30</top>
<width>30</width>
<height>30</height>
<label>L</label>
<onleft>3030</onleft>
<onright>3110</onright>
<onup>3025</onup>
<ondown>3037</ondown>
<align>center</align>
<aligny>center</aligny>
<font>font12</font>
</control>
<!-- Third Row -->
<control type="button" id="3032">
<left>0</left>
<top>60</top>
<width>30</width>
<height>30</height>
<label>M</label>
<onleft>-</onleft>
<onright>3033</onright>
<onup>3026</onup>
<ondown>3038</ondown>
<align>center</align>
<aligny>center</aligny>
<font>font12</font>
</control>
<control type="button" id="3033">
<left>30</left>
<top>60</top>
<width>30</width>
<height>30</height>
<label>N</label>
<onleft>3032</onleft>
<onright>3034</onright>
<onup>3027</onup>
<ondown>3039</ondown>
<align>center</align>
<aligny>center</aligny>
<font>font12</font>
</control>
<control type="button" id="3034">
<left>60</left>
<top>60</top>
<width>30</width>
<height>30</height>
<label>O</label>
<onleft>3033</onleft>
<onright>3035</onright>
<onup>3028</onup>
<ondown>3040</ondown>
<align>center</align>
<aligny>center</aligny>
<font>font12</font>
</control>
<control type="button" id="3035">
<left>90</left>
<top>60</top>
<width>30</width>
<height>30</height>
<label>P</label>
<onleft>3034</onleft>
<onright>3036</onright>
<onup>3029</onup>
<ondown>3041</ondown>
<align>center</align>
<aligny>center</aligny>
<font>font12</font>
</control>
<control type="button" id="3036">
<left>120</left>
<top>60</top>
<width>30</width>
<height>30</height>
<label>Q</label>
<onleft>3035</onleft>
<onright>3037</onright>
<onup>3030</onup>
<ondown>3042</ondown>
<align>center</align>
<aligny>center</aligny>
<font>font12</font>
</control>
<control type="button" id="3037">
<left>150</left>
<top>60</top>
<width>30</width>
<height>30</height>
<label>R</label>
<onleft>3036</onleft>
<onright>3110</onright>
<onup>3031</onup>
<ondown>3043</ondown>
<align>center</align>
<aligny>center</aligny>
<font>font12</font>
</control>
<!-- Forth Row -->
<control type="button" id="3038">
<left>0</left>
<top>90</top>
<width>30</width>
<height>30</height>
<label>S</label>
<onleft>-</onleft>
<onright>3039</onright>
<onup>3032</onup>
<ondown>3044</ondown>
<align>center</align>
<aligny>center</aligny>
<font>font12</font>
</control>
<control type="button" id="3039">
<left>30</left>
<top>90</top>
<width>30</width>
<height>30</height>
<label>T</label>
<onleft>3038</onleft>
<onright>3040</onright>
<onup>3033</onup>
<ondown>3045</ondown>
<align>center</align>
<aligny>center</aligny>
<font>font12</font>
</control>
<control type="button" id="3040">
<left>60</left>
<top>90</top>
<width>30</width>
<height>30</height>
<label>U</label>
<onleft>3039</onleft>
<onright>3041</onright>
<onup>3034</onup>
<ondown>3046</ondown>
<align>center</align>
<aligny>center</aligny>
<font>font12</font>
</control>
<control type="button" id="3041">
<left>90</left>
<top>90</top>
<width>30</width>
<height>30</height>
<label>V</label>
<onleft>3040</onleft>
<onright>3042</onright>
<onup>3035</onup>
<ondown>3047</ondown>
<align>center</align>
<aligny>center</aligny>
<font>font12</font>
</control>
<control type="button" id="3042">
<left>120</left>
<top>90</top>
<width>30</width>
<height>30</height>
<label>W</label>
<onleft>3041</onleft>
<onright>3043</onright>
<onup>3036</onup>
<ondown>3048</ondown>
<align>center</align>
<aligny>center</aligny>
<font>font12</font>
</control>
<control type="button" id="3043">
<left>150</left>
<top>90</top>
<width>30</width>
<height>30</height>
<label>X</label>
<onleft>3042</onleft>
<onright>3110</onright>
<onup>3037</onup>
<ondown>3049</ondown>
<align>center</align>
<aligny>center</aligny>
<font>font12</font>
</control>
<!-- Fifth Row -->
<control type="button" id="3044">
<left>0</left>
<top>120</top>
<width>30</width>
<height>30</height>
<label>Y</label>
<onleft>-</onleft>
<onright>3045</onright>
<onup>3038</onup>
<ondown>3050</ondown>
<align>center</align>
<aligny>center</aligny>
<font>font12</font>
</control>
<control type="button" id="3045">
<left>30</left>
<top>120</top>
<width>30</width>
<height>30</height>
<label>Z</label>
<onleft>3044</onleft>
<onright>3046</onright>
<onup>3039</onup>
<ondown>3051</ondown>
<align>center</align>
<aligny>center</aligny>
<font>font12</font>
</control>
<control type="button" id="3046">
<left>60</left>
<top>120</top>
<width>30</width>
<height>30</height>
<label>$NUMBER[0]</label>
<onleft>3045</onleft>
<onright>3047</onright>
<onup>3040</onup>
<ondown>3052</ondown>
<align>center</align>
<aligny>center</aligny>
<font>font12</font>
</control>
<control type="button" id="3047">
<left>90</left>
<top>120</top>
<width>30</width>
<height>30</height>
<label>$NUMBER[1]</label>
<onleft>3046</onleft>
<onright>3048</onright>
<onup>3041</onup>
<ondown>3053</ondown>
<align>center</align>
<aligny>center</aligny>
<font>font12</font>
</control>
<control type="button" id="3048">
<left>120</left>
<top>120</top>
<width>30</width>
<height>30</height>
<label>$NUMBER[2]</label>
<onleft>3047</onleft>
<onright>3049</onright>
<onup>3042</onup>
<ondown>3054</ondown>
<align>center</align>
<aligny>center</aligny>
<font>font12</font>
</control>
<control type="button" id="3049">
<left>150</left>
<top>120</top>
<width>30</width>
<height>30</height>
<label>$NUMBER[3]</label>
<onleft>3048</onleft>
<onright>3110</onright>
<onup>3043</onup>
<ondown>3055</ondown>
<align>center</align>
<aligny>center</aligny>
<font>font12</font>
</control>
<!-- Sixth Row -->
<control type="button" id="3050">
<left>0</left>
<top>150</top>
<width>30</width>
<height>30</height>
<label>$NUMBER[4]</label>
<onleft>-</onleft>
<onright>3051</onright>
<onup>3044</onup>
<ondown>3056</ondown>
<align>center</align>
<aligny>center</aligny>
<font>font12</font>
</control>
<control type="button" id="3051">
<left>30</left>
<top>150</top>
<width>30</width>
<height>30</height>
<label>$NUMBER[5]</label>
<onleft>3050</onleft>
<onright>3052</onright>
<onup>3045</onup>
<ondown>3056</ondown>
<align>center</align>
<aligny>center</aligny>
<font>font12</font>
</control>
<control type="button" id="3052">
<left>60</left>
<top>150</top>
<width>30</width>
<height>30</height>
<label>$NUMBER[6]</label>
<onleft>3051</onleft>
<onright>3053</onright>
<onup>3046</onup>
<ondown>3057</ondown>
<align>center</align>
<aligny>center</aligny>
<font>font12</font>
</control>
<control type="button" id="3053">
<left>90</left>
<top>150</top>
<width>30</width>
<height>30</height>
<label>$NUMBER[7]</label>
<onleft>3052</onleft>
<onright>3054</onright>
<onup>3047</onup>
<ondown>3057</ondown>
<align>center</align>
<aligny>center</aligny>
<font>font12</font>
</control>
<control type="button" id="3054">
<left>120</left>
<top>150</top>
<width>30</width>
<height>30</height>
<label>$NUMBER[8]</label>
<onleft>3053</onleft>
<onright>3055</onright>
<onup>3048</onup>
<ondown>3058</ondown>
<align>center</align>
<aligny>center</aligny>
<font>font12</font>
</control>
<control type="button" id="3055">
<left>150</left>
<top>150</top>
<width>30</width>
<height>30</height>
<label>$NUMBER[9]</label>
<onleft>3054</onleft>
<onright>3110</onright>
<onup>3049</onup>
<ondown>3058</ondown>
<align>center</align>
<aligny>center</aligny>
<font>font12</font>
</control>
<!-- Seventh Row -->
<control type="button" id="3056">
<left>0</left>
<top>180</top>
<width>60</width>
<height>30</height>
<label>DEL</label>
<onleft>-</onleft>
<onright>3057</onright>
<onup>3050</onup>
<ondown>-</ondown>
<align>center</align>
<aligny>center</aligny>
<font>font12</font>
</control>
<control type="button" id="3057">
<left>60</left>
<top>180</top>
<width>60</width>
<height>30</height>
<label>SPC</label>
<onleft>3056</onleft>
<onright>3058</onright>
<onup>3052</onup>
<ondown>-</ondown>
<align>center</align>
<aligny>center</aligny>
<font>font12</font>
</control>
<control type="button" id="3058">
<left>120</left>
<top>180</top>
<width>60</width>
<height>30</height>
<label>CLR</label>
<onleft>3057</onleft>
<onright>3110</onright>
<onup>3054</onup>
<ondown>-</ondown>
<align>center</align>
<aligny>center</aligny>
<font>font12</font>
</control>
</control>
<!-- Movie Results -->
<control type="label">
<left>265</left>
<top>40</top>
<height>20</height>
<width>190</width>
<label>Movies</label>
<font>font14</font>
<textcolor>white</textcolor>
<angle>-90</angle>
</control>
<control type="fixedlist" id="3110">
<left>280</left>
<top>20</top>
<width>700</width>
<height>170</height>
<onleft>3025</onleft>
<onright>-</onright>
<onup>-</onup>
<ondown>3111</ondown>
<pagecontrol>-</pagecontrol>
<scrolltime>200</scrolltime>
<orientation>horizontal</orientation>
<itemlayout width="120">
<control type="image">
<left>0</left>
<top>0</top>
<width>100</width>
<height>150</height>
<texture>$INFO[Listitem.Icon]</texture>
<bordertexture border="5">button-nofocus.png</bordertexture>
<bordersize>5</bordersize>
</control>
<control type="label">
<left>0</left>
<top>150</top>
<width>100</width>
<height>20</height>
<align>left</align>
<font>font10</font>
<textcolor>FFFFFFFFFF</textcolor>
<label>$INFO[Listitem.Label]</label>
</control>
</itemlayout>
<focusedlayout width="120">
<control type="image">
<left>0</left>
<top>0</top>
<width>100</width>
<height>150</height>
<texture>$INFO[Listitem.Icon]</texture>
<bordertexture border="5">button-nofocus.png</bordertexture>
<bordersize>5</bordersize>
</control>
<control type="image">
<left>0</left>
<top>0</top>
<width>100</width>
<height>150</height>
<texture>$INFO[Listitem.Icon]</texture>
<bordertexture border="5">button-focus.png</bordertexture>
<bordersize>5</bordersize>
<visible>Control.HasFocus(3110)</visible>
</control>
<control type="label">
<left>0</left>
<top>150</top>
<width>100</width>
<height>20</height>
<align>left</align>
<font>font10</font>
<textcolor>FFFFFFFFFF</textcolor>
<label>$INFO[Listitem.Label]</label>
</control>
</focusedlayout>
</control>
<!-- Series -->
<control type="label">
<left>265</left>
<top>240</top>
<height>20</height>
<width>190</width>
<label>Series</label>
<font>font14</font>
<textcolor>white</textcolor>
<angle>-90</angle>
</control>
<control type="fixedlist" id="3111">
<left>280</left>
<top>200</top>
<width>700</width>
<height>170</height>
<onleft>3025</onleft>
<onright>-</onright>
<onup>3110</onup>
<ondown>3112</ondown>
<pagecontrol>-</pagecontrol>
<scrolltime>200</scrolltime>
<orientation>horizontal</orientation>
<itemlayout width="120">
<control type="image">
<left>0</left>
<top>0</top>
<width>100</width>
<height>150</height>
<texture>$INFO[Listitem.Icon]</texture>
<bordertexture border="5">button-nofocus.png</bordertexture>
<bordersize>5</bordersize>
</control>
<control type="label">
<left>0</left>
<top>150</top>
<width>100</width>
<height>20</height>
<align>left</align>
<font>font10</font>
<textcolor>FFFFFFFFFF</textcolor>
<label>$INFO[Listitem.Label]</label>
</control>
</itemlayout>
<focusedlayout width="120">
<control type="image">
<left>0</left>
<top>0</top>
<width>100</width>
<height>150</height>
<texture>$INFO[Listitem.Icon]</texture>
<bordertexture border="5">button-nofocus.png</bordertexture>
<bordersize>5</bordersize>
</control>
<control type="image">
<left>0</left>
<top>0</top>
<width>100</width>
<height>150</height>
<texture>$INFO[Listitem.Icon]</texture>
<bordertexture border="5">button-focus.png</bordertexture>
<bordersize>5</bordersize>
<visible>Control.HasFocus(3111)</visible>
</control>
<control type="label">
<left>0</left>
<top>150</top>
<width>100</width>
<height>20</height>
<align>left</align>
<font>font10</font>
<textcolor>FFFFFFFFFF</textcolor>
<label>$INFO[Listitem.Label]</label>
</control>
</focusedlayout>
</control>
<!-- Episods -->
<control type="label">
<left>265</left>
<top>420</top>
<height>20</height>
<width>190</width>
<label>Episodes</label>
<font>font14</font>
<textcolor>white</textcolor>
<angle>-90</angle>
</control>
<control type="fixedlist" id="3112">
<left>280</left>
<top>380</top>
<width>700</width>
<height>190</height>
<onleft>3025</onleft>
<onright>-</onright>
<onup>3111</onup>
<ondown>-</ondown>
<pagecontrol>-</pagecontrol>
<scrolltime>200</scrolltime>
<orientation>horizontal</orientation>
<itemlayout width="120">
<control type="image">
<left>0</left>
<top>0</top>
<width>100</width>
<height>150</height>
<texture>$INFO[Listitem.Icon]</texture>
<bordertexture border="5">button-nofocus.png</bordertexture>
<bordersize>5</bordersize>
</control>
<control type="label">
<left>0</left>
<top>150</top>
<width>100</width>
<height>20</height>
<align>left</align>
<font>font10</font>
<textcolor>FFFFFFFFFF</textcolor>
<label>$INFO[Listitem.Label]</label>
</control>
<control type="label">
<left>0</left>
<top>170</top>
<width>100</width>
<height>20</height>
<align>left</align>
<font>font10</font>
<textcolor>FFFFFFFFFF</textcolor>
<label>$INFO[Listitem.Label2]</label>
</control>
</itemlayout>
<focusedlayout width="120">
<control type="image">
<left>0</left>
<top>0</top>
<width>100</width>
<height>150</height>
<texture>$INFO[Listitem.Icon]</texture>
<bordertexture border="5">button-nofocus.png</bordertexture>
<bordersize>5</bordersize>
</control>
<control type="image">
<left>0</left>
<top>0</top>
<width>100</width>
<height>150</height>
<texture>$INFO[Listitem.Icon]</texture>
<bordertexture border="5">button-focus.png</bordertexture>
<bordersize>5</bordersize>
<visible>Control.HasFocus(3112)</visible>
</control>
<control type="label">
<left>0</left>
<top>150</top>
<width>100</width>
<height>20</height>
<align>left</align>
<font>font10</font>
<textcolor>FFFFFFFFFF</textcolor>
<label>$INFO[Listitem.Label]</label>
</control>
<control type="label">
<left>0</left>
<top>170</top>
<width>100</width>
<height>20</height>
<align>left</align>
<font>font10</font>
<textcolor>FFFFFFFFFF</textcolor>
<label>$INFO[Listitem.Label2]</label>
</control>
</focusedlayout>
</control>
</controls>
</window>

340
service.py Normal file
View File

@ -0,0 +1,340 @@
import xbmc
import xbmcgui
import xbmcaddon
import urllib
import httplib
import os
import time
import requests
import socket
import threading
import json
from datetime import datetime
import xml.etree.ElementTree as xml
import mimetypes
from threading import Thread
from urlparse import parse_qs
from urllib import urlretrieve
from random import randint
import random
import urllib2
__cwd__ = xbmcaddon.Addon(id='plugin.video.xbmb3c').getAddonInfo('path')
__addon__ = xbmcaddon.Addon(id='plugin.video.xbmb3c')
__language__ = __addon__.getLocalizedString
BASE_RESOURCE_PATH = xbmc.translatePath( os.path.join( __cwd__, 'resources', 'lib' ) )
sys.path.append(BASE_RESOURCE_PATH)
base_window = xbmcgui.Window( 10000 )
from InfoUpdater import InfoUpdaterThread
from NextUpItems import NextUpUpdaterThread
from SuggestedItems import SuggestedUpdaterThread
from RandomItems import RandomInfoUpdaterThread
from ArtworkLoader import ArtworkRotationThread
from ThemeMusic import ThemeMusicThread
from RecentItems import RecentInfoUpdaterThread
from InProgressItems import InProgressUpdaterThread
from WebSocketClient import WebSocketThread
from ClientInformation import ClientInformation
from MenuLoad import LoadMenuOptionsThread
_MODE_BASICPLAY=12
def getAuthHeader():
addonSettings = xbmcaddon.Addon(id='plugin.video.xbmb3c')
deviceName = addonSettings.getSetting('deviceName')
deviceName = deviceName.replace("\"", "_") # might need to url encode this as it is getting added to the header and is user entered data
clientInfo = ClientInformation()
txt_mac = clientInfo.getMachineId()
version = clientInfo.getVersion()
userid = xbmcgui.Window( 10000 ).getProperty("userid")
authString = "MediaBrowser UserId=\"" + userid + "\",Client=\"XBMC\",Device=\"" + deviceName + "\",DeviceId=\"" + txt_mac + "\",Version=\"" + version + "\""
headers = {'Accept-encoding': 'gzip', 'Authorization' : authString}
xbmc.log("XBMB3C Authentication Header : " + str(headers))
return headers
# start some worker threads
newInProgressThread = None
if __addon__.getSetting('useInProgressUpdater') == "true":
newInProgressThread = InProgressUpdaterThread()
newInProgressThread.start()
else:
xbmc.log("XBMB3C Service InProgressUpdater Disabled")
newRecentInfoThread = None
if __addon__.getSetting('useRecentInfoUpdater') == "true":
newRecentInfoThread = RecentInfoUpdaterThread()
newRecentInfoThread.start()
else:
xbmc.log("XBMB3C Service RecentInfoUpdater Disabled")
newRandomInfoThread = None
if __addon__.getSetting('useRandomInfo') == "true":
newRandomInfoThread = RandomInfoUpdaterThread()
newRandomInfoThread.start()
else:
xbmc.log("XBMB3C Service RandomInfo Disabled")
newNextUpThread = None
if __addon__.getSetting('useNextUp') == "true":
newNextUpThread = NextUpUpdaterThread()
newNextUpThread.start()
else:
xbmc.log("XBMB3C Service NextUp Disabled")
newSuggestedThread = None
if __addon__.getSetting('useSuggested') == "true":
newSuggestedThread = SuggestedUpdaterThread()
newSuggestedThread.start()
else:
xbmc.log("XBMB3C Service Suggested Disabled")
newWebSocketThread = None
if __addon__.getSetting('useWebSocketRemote') == "true":
newWebSocketThread = WebSocketThread()
newWebSocketThread.start()
else:
xbmc.log("XBMB3C Service WebSocketRemote Disabled")
newMenuThread = None
if __addon__.getSetting('useMenuLoader') == "true":
newMenuThread = LoadMenuOptionsThread()
newMenuThread.start()
else:
xbmc.log("XBMB3C Service MenuLoader Disabled")
artworkRotationThread = None
if __addon__.getSetting('useBackgroundLoader') == "true":
artworkRotationThread = ArtworkRotationThread()
artworkRotationThread.start()
else:
xbmc.log("XBMB3C Service BackgroundLoader Disabled")
newThemeMusicThread = None
if __addon__.getSetting('useThemeMusic') == "true":
newThemeMusicThread = ThemeMusicThread()
newThemeMusicThread.start()
else:
xbmc.log("XBMB3C Service ThemeMusic Disabled")
newInfoThread = None
if __addon__.getSetting('useInfoLoader') == "true":
newInfoThread = InfoUpdaterThread()
newInfoThread.start()
else:
xbmc.log("XBMB3C Service InfoLoader Disabled")
def deleteItem (url):
return_value = xbmcgui.Dialog().yesno(__language__(30091),__language__(30092))
if return_value:
xbmc.log('Deleting via URL: ' + url)
progress = xbmcgui.DialogProgress()
progress.create(__language__(30052), __language__(30053))
resp = requests.delete(url, data='', headers=getAuthHeader())
deleteSleep=0
while deleteSleep<10:
xbmc.sleep(1000)
deleteSleep=deleteSleep+1
progress.update(deleteSleep*10,__language__(30053))
progress.close()
xbmc.executebuiltin("Container.Refresh")
return 1
else:
return 0
def markWatched(url):
xbmc.log('XBMB3C Service -> Marking watched via: ' + url)
resp = requests.post(url, data='', headers=getAuthHeader())
def markUnWatched(url):
xbmc.log('XBMB3C Service -> Marking watched via: ' + url)
resp = requests.delete(url, data='', headers=getAuthHeader())
def setPosition (url, method):
xbmc.log('XBMB3C Service -> Setting position via: ' + url)
if method == 'POST':
resp = requests.post(url, data='', headers=getAuthHeader())
elif method == 'DELETE':
resp = requests.delete(url, data='', headers=getAuthHeader())
def stopTranscoding(url):
xbmc.log('XBMB3C Service -> Stopping transcoding: ' + url)
resp = requests.delete(url, data='', headers=getAuthHeader())
def hasData(data):
if(data == None or len(data) == 0 or data == "None"):
return False
else:
return True
def stopAll(played_information):
if(len(played_information) == 0):
return
addonSettings = xbmcaddon.Addon(id='plugin.video.xbmb3c')
xbmc.log ("XBMB3C Service -> played_information : " + str(played_information))
for item_url in played_information:
data = played_information.get(item_url)
if(data != None):
xbmc.log ("XBMB3C Service -> item_url : " + item_url)
xbmc.log ("XBMB3C Service -> item_data : " + str(data))
watchedurl = data.get("watchedurl")
positionurl = data.get("positionurl")
deleteurl = data.get("deleteurl")
runtime = data.get("runtime")
currentPossition = data.get("currentPossition")
item_id = data.get("item_id")
if(currentPossition != None and hasData(runtime) and hasData(positionurl) and hasData(watchedurl)):
runtimeTicks = int(runtime)
xbmc.log ("XBMB3C Service -> runtimeticks:" + str(runtimeTicks))
percentComplete = (currentPossition * 10000000) / runtimeTicks
markPlayedAt = float(addonSettings.getSetting("markPlayedAt")) / 100
xbmc.log ("XBMB3C Service -> Percent Complete:" + str(percentComplete) + " Mark Played At:" + str(markPlayedAt))
if (percentComplete > markPlayedAt):
gotDeleted = 0
if(deleteurl != None and deleteurl != ""):
xbmc.log ("XBMB3C Service -> Offering Delete:" + str(deleteurl))
gotDeleted = deleteItem(deleteurl)
if(gotDeleted == 0):
setPosition(positionurl + '/Progress?PositionTicks=0', 'POST')
if(newWebSocketThread != None):
newWebSocketThread.playbackStopped(item_id, str(0))
markWatched(watchedurl)
else:
#markUnWatched(watchedurl) # this resets the LastPlayedDate and that causes issues with sortby PlayedDate so I removed it for now
if(newWebSocketThread != None):
newWebSocketThread.playbackStopped(item_id, str(int(currentPossition * 10000000)))
setPosition(positionurl + '?PositionTicks=' + str(int(currentPossition * 10000000)), 'DELETE')
if(newNextUpThread != None):
newNextUpThread.updateNextUp()
if(artworkRotationThread != None):
artworkRotationThread.updateActionUrls()
played_information.clear()
# stop transcoding - todo check we are actually transcoding?
clientInfo = ClientInformation()
txt_mac = clientInfo.getMachineId()
url = ("http://%s:%s/mediabrowser/Videos/ActiveEncodings" % (addonSettings.getSetting('ipaddress'), addonSettings.getSetting('port')))
url = url + '?DeviceId=' + txt_mac
stopTranscoding(url)
class Service( xbmc.Player ):
played_information = {}
def __init__( self, *args ):
xbmc.log("XBMB3C Service -> starting monitor service")
self.played_information = {}
pass
def onPlayBackStarted( self ):
# Will be called when xbmc starts playing a file
stopAll(self.played_information)
currentFile = xbmc.Player().getPlayingFile()
xbmc.log("XBMB3C Service -> onPlayBackStarted" + currentFile)
WINDOW = xbmcgui.Window( 10000 )
watchedurl = WINDOW.getProperty(currentFile+"watchedurl")
deleteurl = WINDOW.getProperty(currentFile+"deleteurl")
positionurl = WINDOW.getProperty(currentFile+"positionurl")
runtime = WINDOW.getProperty(currentFile+"runtimeticks")
item_id = WINDOW.getProperty(currentFile+"item_id")
# reset all these so they dont get used is xbmc plays a none
# xbmb3c MB item
# WINDOW.setProperty(currentFile+"watchedurl", "")
# WINDOW.setProperty(currentFile+"deleteurl", "")
# WINDOW.setProperty(currentFile+"positionurl", "")
# WINDOW.setProperty(currentFile+"runtimeticks", "")
# WINDOW.setProperty(currentFile+"item_id", "")
if(item_id == None or len(item_id) == 0):
return
if(newWebSocketThread != None):
newWebSocketThread.playbackStarted(item_id)
if (watchedurl != "" and positionurl != ""):
data = {}
data["watchedurl"] = watchedurl
data["deleteurl"] = deleteurl
data["positionurl"] = positionurl
data["runtime"] = runtime
data["item_id"] = item_id
self.played_information[currentFile] = data
xbmc.log("XBMB3C Service -> ADDING_FILE : " + currentFile)
xbmc.log("XBMB3C Service -> ADDING_FILE : " + str(self.played_information))
# reset in progress possition
setPosition(positionurl + '/Progress?PositionTicks=0', 'POST')
def onPlayBackEnded( self ):
# Will be called when xbmc stops playing a file
xbmc.log("XBMB3C Service -> onPlayBackEnded")
stopAll(self.played_information)
def onPlayBackStopped( self ):
# Will be called when user stops xbmc playing a file
xbmc.log("XBMB3C Service -> onPlayBackStopped")
stopAll(self.played_information)
monitor = Service()
lastProgressUpdate = datetime.today()
addonSettings = xbmcaddon.Addon(id='plugin.video.xbmb3c')
if socket.gethostname() != None and socket.gethostname() != '' and addonSettings.getSetting("deviceName") == 'XBMB3C':
addonSettings.setSetting("deviceName", socket.gethostname())
while not xbmc.abortRequested:
if xbmc.Player().isPlaying():
try:
playTime = xbmc.Player().getTime()
currentFile = xbmc.Player().getPlayingFile()
if(monitor.played_information.get(currentFile) != None):
monitor.played_information[currentFile]["currentPossition"] = playTime
# send update
td = datetime.today() - lastProgressUpdate
secDiff = td.seconds
if(secDiff > 10):
if(monitor.played_information.get(currentFile) != None and monitor.played_information.get(currentFile).get("item_id") != None):
item_id = monitor.played_information.get(currentFile).get("item_id")
if(newWebSocketThread != None):
newWebSocketThread.sendProgressUpdate(item_id, str(int(playTime * 10000000)))
lastProgressUpdate = datetime.today()
except Exception, e:
xbmc.log("XBMB3C Service -> Exception in Playback Monitor : " + str(e))
pass
xbmc.sleep(1000)
xbmcgui.Window(10000).setProperty("XBMB3C_Service_Timestamp", str(int(time.time())))
# stop the WebSocket client
if(newWebSocketThread != None):
newWebSocketThread.stopClient()
# stop the image proxy
keepServing = False
xbmc.log("XBMB3C Service -> Service shutting down")