initial commit - first version

This commit is contained in:
Marcel van der Veldt 2015-03-13 22:24:59 +01:00
parent fdbfc9455b
commit 860bdfbbd8
24 changed files with 3353 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
-------------------------------------------------------------------------
-------------------------------------------------------------------------

25
addon.xml Normal file
View File

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<addon id="plugin.video.mb3sync"
name="MediaBrowser Syncer"
version="0.0.1"
provider-name="mediabrowser.tv">
<requires>
<import addon="xbmc.python" version="2.1.0"/>
</requires>
<extension point="xbmc.python.pluginsource"
library="default.py">
<provides>executable video audio image</provides>
</extension>
<extension point="xbmc.service" library="service.py" start="login">
</extension>
<extension point="xbmc.addon.metadata">
<platform>all</platform>
<language>en</language>
<license>GNU GENERAL PUBLIC LICENSE. Version 2, June 1991</license>
<forum></forum>
<website>http://mediabrowser.tv/</website>
<source></source>
<summary lang="en"></summary>
<description lang="en"></description>
</extension>
</addon>

2
changelog.txt Normal file
View File

@ -0,0 +1,2 @@
0.0.1
- initital alpha version

27
default.py Normal file
View File

@ -0,0 +1,27 @@
import xbmcaddon
import xbmcplugin
import xbmc
import xbmcgui
import os
import threading
import json
import urllib
addonSettings = xbmcaddon.Addon(id='plugin.video.mb3sync')
cwd = addonSettings.getAddonInfo('path')
BASE_RESOURCE_PATH = xbmc.translatePath( os.path.join( cwd, 'resources', 'lib' ) )
sys.path.append(BASE_RESOURCE_PATH)
WINDOW = xbmcgui.Window( 10000 )
import Utils as utils
from PlaybackUtils import PlaybackUtils
# get the actions...
params=utils.get_params(sys.argv[2])
mode = params.get('mode',"")
id = params.get('id',"")
if mode == "play":
PlaybackUtils().PLAY(id)

BIN
fanart.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 386 KiB

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,239 @@
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<strings>
<string id="30000">Primaire server adres:</string>
<string id="30001">Enkele mappen automatisch openen:</string>
<string id="30002">Afspelen van stream ipv SMB:</string>
<string id="30004">Log niveau:</string>
<string id="30005">Gebruikersnaam: </string>
<string id="30006">Wachtwoord: </string>
<string id="30007">Samba gebruikersnaam: </string>
<string id="30008">Samba wachtwoord: </string>
<string id="30009">Transcode: </string>
<string id="30010">Prestatie profilering inschakelen:</string>
<string id="30011">Lokale cache systeem</string>
<string id="30014">MediaBrowser</string>
<string id="30015">Netwerk</string>
<string id="30016">Apparaatnaam</string>
<string id="30022">Geavanceerd</string>
<string id="30024">Gebruikersnaam:</string>
<string id="30025">Wachtwoord:</string>
<string id="30026">Gebruik SIMPLEJSON ipv JSON</string>
<string id="30030">Poortnummer:</string>
<string id="30036">Aantal recente films die getoond worden:</string>
<string id="30037">Aantal recente TV-series die getoond worden:</string>
<string id="30035">Aantal recente muziekalbums die getoond worden:</string>
<string id="30038">Markeer als bekeken bij starten van afspelen:</string>
<string id="30039">Gebruik seizoen poster bij afleveringen</string>
<string id="30040">Genre filter ...</string>
<string id="30041">Speel alles vanaf hier</string>
<string id="30042">Vernieuwen</string>
<string id="30043">Wissen</string>
<string id="30046">Voeg film toe aan CouchPotato</string>
<string id="30044">Ongeldige gebruikersnaam/wachtwoord</string>
<string id="30045">Gebruikersnaam niet gevonden</string>
<string id="30052">Wissen...</string>
<string id="30053">Wacht op server voor wissen</string>
<string id="30059">Server standaard</string>
<string id="30060">Titel</string>
<string id="30061">Jaar</string>
<string id="30062">Premiere datum</string>
<string id="30063">Datum toegevoegd</string>
<string id="30064">Critici beoordeling</string>
<string id="30065">Community beoordeling</string>
<string id="30066">Aantal keer bekeken</string>
<string id="30067">Budget</string>
<string id="30068">Sorteer op</string>
<string id="30069">Geen</string>
<string id="30070">Actie</string>
<string id="30071">Avontuur</string>
<string id="30072">Animatie</string>
<string id="30073">Misdaad</string>
<string id="30074">Comedy</string>
<string id="30075">Documentaire</string>
<string id="30076">Drama</string>
<string id="30077">Fantasie</string>
<string id="30078">Nederlands</string>
<string id="30079">Historie</string>
<string id="30080">Horror</string>
<string id="30081">Muziek</string>
<string id="30082">Musical</string>
<string id="30083">Mysterie</string>
<string id="30084">Romantiek</string>
<string id="30085">Science Fiction</string>
<string id="30086">Kort</string>
<string id="30087">Spanning</string>
<string id="30088">Thriller</string>
<string id="30089">Western</string>
<string id="30090">Genre filter</string>
<string id="30091">Bevestig wissen</string>
<string id="30092">Dit item wissen ? Dit zal het bestand volledig verwijderen.</string>
<string id="30093">Markeer als bekeken</string>
<string id="30094">Mark als onbekeken</string>
<string id="30095">Voeg toe aan favorieten</string>
<string id="30096">Verwijder uit favorieten</string>
<string id="30097">Sorteer op...</string>
<string id="30098">Sorteer oplopend</string>
<string id="30099">Sorteer aflopend</string>
<string id="30100">Toon acteurs</string>
<!-- resume dialog -->
<string id="30105">Hervatten</string>
<string id="30106">Hervatten vanaf</string>
<string id="30107">Start vanaf begin</string>
<string id="30110">Interface</string>
<string id="30111">Inclusief stream info</string>
<string id="30112">Inclusief personen</string>
<string id="30113">Inclusief filminfo</string>
<string id="30114">Bij hervatten aantal seconden terugspringen</string>
<string id="30115">Markeer als bekeken wanneer na percentage gestopt</string>
<string id="30116">Inclusief aantal en afspeel tellers</string>
<string id="30117"> - Achtergrond plaatjes verversen (secondes)</string>
<string id="30118">Inclusief hervat-percentage</string>
<string id="30119">Afleveringnummer tonen in titel</string>
<string id="30120">Toon voortgang</string>
<string id="30121">Laden van 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">Toon wismogelijkheid na bekijken van aflevering</string>
<string id="30128">Afspeelfout!</string>
<string id="30129">Dit item kan niet worden afgespeeld</string>
<string id="30130">Lockaal pad gedetecteerd</string>
<string id="30131">De MB3 bibliotheek bevat lokale paden. U moet UNC-paden gebruiken of afspelen van stream inschakelen. Pad: </string>
<string id="30132">Waarschuwing</string>
<string id="30133">Debug logging ingeschakeld.</string>
<string id="30134">Dit heeft effect op de performance.</string>
<string id="30135">Fout</string>
<string id="30136">XBMB3C service werkt niet</string>
<string id="30137">Herstart XBMC aub</string>
<string id="30138">Zoeken</string>
<string id="30139">Schakel Themamuziek in (vereist herstart)</string>
<string id="30140"> - Herhalen van themamuziek</string>
<string id="30141">Activeer achtergrondafbeelding (vereist herstart)</string>
<string id="30142">Services</string>
<string id="30143">Activeer Info Loader (vereist herstart)</string>
<string id="30144">Activeer Menu Loader (vereist herstart)</string>
<string id="30145">Activeer WebSocket Remote (vereist herstart)</string>
<string id="30146">Activeer In Progress Loader (vereist herstart)</string>
<string id="30147">Activeer Recent Info Loader (vereist herstart)</string>
<string id="30148">Activeer Random Loader (vereist herstart)</string>
<string id="30149">Activeer Next Up Loader (vereist herstart)</string>
<string id="30150">Skin ondersteund het vastleggen van views niet</string>
<string id="30151">Selecteer item actie (vereist herstart)</string>
<string id="30152">Toon indactors</string>
<string id="30153"> - Toon bekeken indator</string>
<string id="30154"> - Toon aantal onbekeken indicator</string>
<string id="30155"> - Toon afspeel-percentage indicator</string>
<string id="30156">Sorteer volgende (NextUp) op titel</string>
<string id="30157">Deactiveer speciale afbeeldingen (bv CoverArt)</string>
<string id="30158">Metadata</string>
<string id="30159">Afbeeldingen</string>
<string id="30160">Video kwaliteit</string>
<string id="30161">Activeer Suggested Loader (vereist herstart)</string>
<string id="30162">Seizoen tonen in titel</string>
<string id="30163">Seizoenen verbergen</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">Alle films</string>
<string id="30171">Alle TV</string>
<string id="30172">Alle Muziek</string>
<string id="30173">Kanalen</string>
<string id="30174">Recent toegevoegde films</string>
<string id="30175">Recent toegevoegde afleveringen</string>
<string id="30176">Recent toegevoegde albums</string>
<string id="30177">Niet afgekeken films</string>
<string id="30178">Niet afgekeken afleveringen</string>
<string id="30179">NextUp afleveringen</string>
<string id="30180">Favoriete films</string>
<string id="30181">Favoriete TV-series</string>
<string id="30182">Favoriete afleveringen</string>
<string id="30183">Vaak afgespeelde albums</string>
<string id="30184">Upcoming TV</string>
<string id="30185">BoxSets</string>
<string id="30186">Trailers</string>
<string id="30187">Muziek videos</string>
<string id="30188">Fotos</string>
<string id="30189">Onbekeken films</string>
<string id="30190">Film Genres</string>
<string id="30191">Film Studios</string>
<string id="30192">Film Acteurs</string>
<string id="30193">Onbekeken afleveringen</string>
<string id="30194">TV Genres</string>
<string id="30195">TV Networks</string>
<string id="30196">TV Acteurs</string>
<string id="30197">Afspeellijsten</string>
<string id="30198">Zoeken</string>
<string id="30199">Views instellen</string>
<string id="30200">Selecteer gebruiker</string>
<string id="30201">Profilering ingeschakeld.</string>
<string id="30202">Svp onthouden om weer uit te schakelen na het testen.</string>
<string id="30203">Error in ArtworkRotationThread</string>
<string id="30204">Kan niet verbinden met server</string>
<string id="30205">Error in LoadMenuOptionsThread</string>
<string id="30206">Activeer Playlists Loader (vereist herstart)</string>
<string id="30207">Liedjes</string>
<string id="30208">Albums</string>
<string id="30209">Album artiesten</string>
<string id="30210">Artiesten</string>
<string id="30211">Muziek Genres</string>
<string id="30212">Schakel Themavideos in (vereist herstart)</string>
<string id="30213"> - Herhalen van themavideos</string>
<string id="30214">Schakel het forceren van view uit</string>
<string id="30215">Schakel snelle modus in (beta)</string>
<string id="30216">Automatisch resterende afleveringen in een seizoen afspelen</string>
<string id="30217">Boxsets tonen in de overzichten (vereist herstart)</string>
<string id="30218">Afbeeldingen comprimeren</string>
<string id="30219">Activeer Skin Helper (vereust herstart)</string>
<string id="30220">Laatste </string>
<string id="30221">Bezig </string>
<string id="30222">Volgende </string>
<string id="30223">Gebruikerweergaven </string>
<!-- Default views -->
<string id="30300">Actief</string>
<string id="30301">Herstel standaard</string>
<string id="30302">Films</string>
<string id="30303">BoxSets</string>
<string id="30304">Trailers</string>
<string id="30305">Series</string>
<string id="30306">Seizoenen</string>
<string id="30307">Afleveringen</string>
<string id="30308">Muziek - artiesten</string>
<string id="30309">Muziek - albums</string>
<string id="30310">Muziekvideos</string>
<string id="30311">Muziek - liedjes</string>
<string id="30312">Kanalen</string>
</strings>

View File

@ -0,0 +1,252 @@
<?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">Network Username: </string>
<string id="30008">Network Password: </string>
<string id="30009">Transcode: </string>
<string id="30010">Enable Performance Profiling</string>
<string id="30011">Local caching system</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>
<!-- Runtime added as 30226 below -->
<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>
<!-- resume dialog -->
<string id="30105">Resume</string>
<string id="30106">Resume from</string>
<string id="30107">Start from beginning</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"> - Offer delete when stopped 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">Monitoring service is not running</string>
<string id="30137">If you have just installed please restart Kodi</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 server</string>
<string id="30205">Error in LoadMenuOptionsThread</string>
<string id="30206">Enable Playlists Loader (Requires Restart)</string>
<string id="30207">Songs</string>
<string id="30208">Albums</string>
<string id="30209">Album Artists</string>
<string id="30210">Artists</string>
<string id="30211">Music Genres</string>
<string id="30212">Enable Theme Videos (Requires Restart)</string>
<string id="30213"> - Loop Theme Videos</string>
<string id="30214">Disable forced view</string>
<string id="30215">Enable Fast Processing</string>
<string id="30216">AutoPlay remaining episodes in a season</string>
<string id="30217">Show boxsets collapsed in views (Requires Restart)</string>
<string id="30218">Compress Artwork</string>
<string id="30219">Enable Skin Helper (Requires Restart)</string>
<string id="30220">Latest </string>
<string id="30221">In Progress </string>
<string id="30222">NextUp </string>
<string id="30223">User Views</string>
<string id="30224">Report Metrics</string>
<string id="30225">Use Kodi Sorting</string>
<string id="30226">Runtime</string>
<string id="30227">Random Movies</string>
<string id="30228">Random Episodes</string>
<string id="30229">Skin Compatibility Warning</string>
<string id="30230">Your current skin is not fully compatible.</string>
<string id="30231">For a better experience use a skin from the forum.</string>
<string id="30232">http://tinyurl.com/knfus2x</string>
<string id="30233">Don't Show Skin Compatibility Message</string>
<string id="30234">Add Show Name (Season + Episode)</string>
<!-- Default views -->
<string id="30300">Active</string>
<string id="30301">Clear Settings</string>
<string id="30302">Movies</string>
<string id="30303">BoxSets</string>
<string id="30304">Trailers</string>
<string id="30305">Series</string>
<string id="30306">Seasons</string>
<string id="30307">Episodes</string>
<string id="30308">Music Artists</string>
<string id="30309">Music Albums</string>
<string id="30310">Music Videos</string>
<string id="30311">Music Tracks</string>
<string id="30312">Channels</string>
</strings>

View File

@ -0,0 +1,227 @@
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<strings>
<string id="30000">IP-Adresse des Servers</string>
<string id="30001">Automatisches Öffnen von Ordnern mit einem Eintrag</string>
<string id="30002">Via HTTP abspielen statt SMB/NFS:</string>
<string id="30004">Log Level:</string>
<string id="30005">Benutzername: </string>
<string id="30006">Passwort: </string>
<string id="30007">Samba-Benutzername: </string>
<string id="30008">Samba-Passwort: </string>
<string id="30009">Transkodieren: </string>
<string id="30010">Performancemessung aktivieren</string>
<string id="30011">Caching-Mechanismus</string>
<string id="30014">MediaBrowser</string>
<string id="30015">Netzwerk</string>
<string id="30016">Gerätename</string>
<string id="30022">Erweitert</string>
<string id="30024">Benutzername:</string>
<string id="30025">Passwort:</string>
<string id="30030">Portnummer:</string>
<string id="30036">Anzahl der zuletzt hinzugefügten Filme:</string>
<string id="30037">Anzahl der zuletzt hinzugefügten Episoden:</string>
<string id="30035">Anzahl der zuletzt hinzugefügten Alben:</string>
<string id="30038">Bei Start der Wiedergabe als 'gesehen' markieren:</string>
<string id="30039">Staffelposter für Episoden nutzen</string>
<string id="30040">Genre Filter ...</string>
<string id="30041">Alles von hier abspielen</string>
<string id="30042">Aktualisieren</string>
<string id="30043">Löschen</string>
<string id="30046">Film zu CouchPotato hinzufügen</string>
<string id="30044">Benutzername/Passwort falsch</string>
<string id="30045">Benutzername nicht gefunden</string>
<string id="30052">Lösche</string>
<string id="30053">Lösche von Server</string>
<string id="30059">Server Standard</string>
<string id="30060">Titel</string>
<string id="30061">Jahr</string>
<string id="30062">Premierendatum</string>
<string id="30063">Datum hinzugefügt</string>
<string id="30064">Bewertung</string>
<string id="30065">Zuschauerbewertung</string>
<string id="30066">Abspielzähler</string>
<string id="30067">Budget</string>
<string id="30068">Sortiere nach</string>
<string id="30069">Kein Filter</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">Löschen von Dateien bestätigen?</string>
<string id="30092">Diesen Eintrag löschen? Diese Aktion löscht die Mediendatei und damit verbundene Daten.</string>
<string id="30093">Als 'gesehen' markieren</string>
<string id="30094">Als 'ungesehen' markieren</string>
<string id="30095">Zu Favoriten hinzufügen</string>
<string id="30096">Von Favoriten entfernen</string>
<string id="30097">Sortiere nach ...</string>
<string id="30098">Sortierreihenfolge absteigend</string>
<string id="30099">Sortierreihenfolge aufsteigend</string>
<string id="30100">Zeige Mitwirkende</string>
<!-- resume dialog -->
<string id="30105">Fortsetzen</string>
<string id="30106">Fortsetzen bei</string>
<string id="30107">Am Anfang starten</string>
<string id="30110">Benutzeroberfläche</string>
<string id="30111">Lade Streaminformationen</string>
<string id="30112">Lade Darsteller</string>
<string id="30113">Lade Inhaltsübersicht</string>
<string id="30114">Rücksprung bei Fortsetzen</string>
<string id="30115">Bei Stop nach x % als 'gesehen' markieren</string>
<string id="30116">Medien- und 'Abgespielt'-Zähler hinzufügen</string>
<string id="30117"> - Aktualisierungsintervall von Hintergrundbildern (Sekunden)</string>
<string id="30118">Prozentanzeige für Fortsetzen</string>
<string id="30119">Episodennummer hinzufügen</string>
<string id="30120">Ladefortschritt anzeigen</string>
<string id="30121">Lade Inhalt</string>
<string id="30122">Lade Daten</string>
<string id="30123">Verarbeite Json Daten</string>
<string id="30124">Lade Json Daten</string>
<string id="30125">Fertig</string>
<string id="30126">Verarbeite Eintrag : </string>
<string id="30127">Löschen von gesehenen Episoden anbieten</string>
<string id="30128">Abspielfehler</string>
<string id="30129">Dieser Eintrag ist nicht abspielbar</string>
<string id="30130">Lokaler Pfad erkannt</string>
<string id="30132">Warnung</string>
<string id="30133">Debug Logging aktiviert.</string>
<string id="30134">Dies beeinträchtigt die Performance.</string>
<string id="30135">Fehler</string>
<string id="30136">XBMB3C-Service läuft nicht</string>
<string id="30137">Bitte XBMC neustarten</string>
<string id="30138">Suche</string>
<string id="30139">Themen-Musik aktivieren (Erfordert Neustart)</string>
<string id="30140"> - Themen-Musik in Schleife abspielen</string>
<string id="30141">Laden im Hintergrund aktivieren (Erfordert Neustart)</string>
<string id="30142">Dienste</string>
<string id="30143">Info-Loader aktivieren (Erfordert Neustart)</string>
<string id="30144">Menü-Loader aktivieren (Erfordert Neustart)</string>
<string id="30145">WebSocket Fernbedienung aktivieren (Erfordert Neustart)</string>
<string id="30146">'Laufende Medien'-Loader aktivieren (Erfordert Neustart)</string>
<string id="30147">'Zuletzt hinzugefügt' Loader aktivieren (Erfordert Neustart)</string>
<string id="30148">'Zufallsmedien'-Loader aktivieren (Erfordert Neustart)</string>
<string id="30149">'Nächste'-Loader aktivieren (Erfordert Neustart)</string>
<string id="30150">Skin unterstützt das Setzen von Views nicht</string>
<string id="30151">Aktion bei Auswahl (Erfordert Neustart)</string>
<string id="30152">Indikatoren</string>
<string id="30153"> - 'Gesehen'-Indikator anzeigen</string>
<string id="30154"> - Zähler für ungesehene Medien anzeigen</string>
<string id="30155"> - Abspiel-Prozentanzeige aktivieren</string>
<string id="30156">Sortiere 'Nächste' nach Serientitel</string>
<string id="30157">Deaktiviere erweiterte Bilder (z.B. CoverArt)</string>
<string id="30158">Metadaten</string>
<string id="30159">Grafiken</string>
<string id="30160">Videoqualität</string>
<string id="30161">'Empfohlen'-Loader aktivieren (Erfordert Neustart)</string>
<string id="30162">Staffelnummer hinzufügen</string>
<string id="30163">Serienstaffeln reduzieren</string>
<string id="30164">Direkte Wiedergabe - HTTP</string>
<string id="30165">Direkte Wiedergabe</string>
<string id="30166">Transkodierung</string>
<string id="30167">Serversuche erfolgreich</string>
<string id="30168">Server gefunden</string>
<string id="30169">Addresse : </string>
<string id="30170">Alle Filme</string>
<string id="30171">Alle Serien</string>
<string id="30172">Alles an Musik</string>
<string id="30173">Kanäle</string>
<string id="30174">Zuletzt hinzugefügte Filme</string>
<string id="30175">Zuletzt hinzugefügte Episoden</string>
<string id="30176">Zuletzt hinzugefügte Alben</string>
<string id="30177">Begonnene Filme</string>
<string id="30178">Begonnene Episoden</string>
<string id="30179">Nächste Episoden</string>
<string id="30180">Favorisierte Filme</string>
<string id="30181">Favorisierte Serien</string>
<string id="30182">Favorisierte Episoden</string>
<string id="30183">Häufig gespielte Alben</string>
<string id="30184">Anstehende Serien</string>
<string id="30185">Sammlungen</string>
<string id="30186">Trailer</string>
<string id="30187">Musikvideos</string>
<string id="30188">Fotos</string>
<string id="30189">Ungesehene Filme</string>
<string id="30190">Filmgenres</string>
<string id="30191">Studios</string>
<string id="30192">Filmdarsteller</string>
<string id="30193">Ungesehene Episoden</string>
<string id="30194">Seriengenres</string>
<string id="30195">Fernsehsender</string>
<string id="30196">Seriendarsteller</string>
<string id="30197">Wiedergabelisten</string>
<string id="30198">Suche</string>
<string id="30199">Ansichten festlegen</string>
<string id="30200">Wähle Benutzer</string>
<string id="30201">Messung aktiviert.</string>
<string id="30202">Bitte daran denken, nach dem Testen wieder zu deaktivieren.</string>
<string id="30203">Fehler in ArtworkRotationThread</string>
<string id="30204">Verbindung zum Server fehlgeschlagen</string>
<string id="30205">Fehler in LoadMenuOptionsThread</string>
<string id="30206">'Playlist'-Loader aktivieren (Erfordert Neustart)</string>
<string id="30207">Songs</string>
<string id="30208">Alben</string>
<string id="30209">Album-Interpreten</string>
<string id="30210">Interpreten</string>
<string id="30211">Musik-Genres</string>
<string id="30212">Themen-Videos aktivieren (Erfordert Neustart)</string>
<string id="30213"> - Themen-Videos in Schleife abspielen</string>
<string id="30214">Festgelegte Ansichten deaktivieren</string>
<string id="30215">Schnelleres Laden der Daten aktivieren</string>
<string id="30216">Spiele weitere Episoden einer Staffel automatisch ab</string>
<string id="30217">Aktiviere gruppierte Darstellung von Sammlungen (Erfordert Neustart)</string>
<string id="30218">Bilder komprimieren</string>
<!-- Default views -->
<string id="30300">Aktiviert</string>
<string id="30301">Zurücksetzen</string>
<string id="30302">Filme</string>
<string id="30303">BoxSets</string>
<string id="30304">Trailer</string>
<string id="30305">Serien</string>
<string id="30306">Staffeln</string>
<string id="30307">Episoden</string>
<string id="30308">Interpreten</string>
<string id="30309">Alben</string>
<string id="30310">Musikvideos</string>
<string id="30311">Musikstücke</string>
</strings>

250
resources/lib/API.py Normal file
View File

@ -0,0 +1,250 @@
# API.py
# This class helps translate more complex cases from the MediaBrowser API to the XBMC API
from datetime import datetime
class API():
def getPeople(self, item):
# Process People
director=''
writer=''
cast=[]
people = item.get("People")
if(people != None):
for person in people:
if(person.get("Type") == "Director"):
director = director + person.get("Name") + ' '
if(person.get("Type") == "Writing"):
writer = person.get("Name")
if(person.get("Type") == "Writer"):
writer = person.get("Name")
if(person.get("Type") == "Actor"):
Name = person.get("Name")
Role = person.get("Role")
if Role == None:
Role = ''
cast.append(Name)
return {'Director' : director,
'Writer' : writer,
'Cast' : cast
}
def getTimeInfo(self, item):
resumeTime = ''
userData = item.get("UserData")
PlaybackPositionTicks = '100'
if userData.get("PlaybackPositionTicks") != None:
PlaybackPositionTicks = str(userData.get("PlaybackPositionTicks"))
reasonableTicks = int(userData.get("PlaybackPositionTicks")) / 1000
resumeTime = reasonableTicks / 10000
try:
tempDuration = str(int(item.get("RunTimeTicks", "0"))/(10000000*60))
except TypeError:
try:
tempDuration = str(int(item.get("CumulativeRunTimeTicks"))/(10000000*60))
except TypeError:
tempDuration = "0"
cappedPercentage = None
resume=0
percentage=0
if (resumeTime != "" and int(resumeTime) > 0):
duration = float(tempDuration)
if(duration > 0):
resume = float(resumeTime) / 60
percentage = int((resume / duration) * 100.0)
return {'Duration' : tempDuration,
'TotalTime' : tempDuration,
'Percent' : str(percentage),
'ResumeTime' : str(resume)
}
def getStudio(self, item):
# Process Studio
studio = ""
if item.get("SeriesStudio") != None and item.get("SeriesStudio") != '':
studio = item.get("SeriesStudio")
if studio == "":
studios = item.get("Studios")
if(studios != None):
for studio_string in studios:
if studio=="": #Just take the first one
temp=studio_string.get("Name")
studio=temp.encode('utf-8')
return studio
def getMediaStreams(self, item, mediaSources=False):
# Process MediaStreams
channels = ''
videocodec = ''
audiocodec = ''
height = ''
width = ''
aspectratio = '1:1'
aspectfloat = 1.85
if mediaSources == True:
mediaSources = item.get("MediaSources")
if(mediaSources != None):
MediaStreams = mediaSources[0].get("MediaStreams")
else:
MediaStreams = None
else:
MediaStreams = item.get("MediaStreams")
if(MediaStreams != None):
#mediaStreams = MediaStreams[0].get("MediaStreams")
if(MediaStreams != None):
for mediaStream in MediaStreams:
if(mediaStream.get("Type") == "Video"):
videocodec = mediaStream.get("Codec")
height = str(mediaStream.get("Height"))
width = str(mediaStream.get("Width"))
aspectratio = mediaStream.get("AspectRatio")
if aspectratio != None and len(aspectratio) >= 3:
try:
aspectwidth,aspectheight = aspectratio.split(':')
aspectfloat = float(aspectwidth) / float(aspectheight)
except:
aspectfloat = 1.85
if(mediaStream.get("Type") == "Audio"):
audiocodec = mediaStream.get("Codec")
channels = mediaStream.get("Channels")
return {'channels' : str(channels),
'videocodec' : videocodec,
'audiocodec' : audiocodec,
'height' : height,
'width' : width,
'aspectratio' : str(aspectfloat)
}
def getUserData(self, item):
userData = item.get("UserData")
resumeTime = 0
if(userData != None):
if userData.get("Played") != True:
watched="True"
else:
watched="False"
if userData.get("IsFavorite") == True:
favorite="True"
else:
favorite="False"
if(userData.get("Played") == True):
playcount="1"
else:
playcount="0"
if userData.get('UnplayedItemCount') != None:
UnplayedItemCount = userData.get('UnplayedItemCount')
else:
UnplayedItemCount = "0"
if userData.get('PlaybackPositionTicks') != None:
PlaybackPositionTicks = userData.get('PlaybackPositionTicks')
else:
PlaybackPositionTicks = ''
return {'Watched' : watched,
'Favorite' : favorite,
'PlayCount': playcount,
'UnplayedItemCount' : UnplayedItemCount,
'PlaybackPositionTicks' : str(PlaybackPositionTicks)
}
def getGenre(self,item):
genre = ""
genres = item.get("Genres")
if genres != None and genres != []:
for genre_string in genres:
if genre == "": #Just take the first genre
genre = genre_string
else:
genre = genre + " / " + genre_string
elif item.get("SeriesGenres") != None and item.get("SeriesGenres") != '':
genres = item.get("SeriesGenres")
if genres != None and genres != []:
for genre_string in genres:
if genre == "": #Just take the first genre
genre = genre_string
else:
genre = genre + " / " + genre_string
return genre
def getName(self, item):
Temp = item.get("Name")
if Temp == None:
Temp = ""
Name=Temp.encode('utf-8')
return Name
def getRecursiveItemCount(self, item):
if item.get("RecursiveItemCount") != None:
return str(item.get("RecursiveItemCount"))
else:
return "0"
def getSeriesName(self, item):
Temp = item.get("SeriesName")
if Temp == None:
Temp = ""
Name=Temp.encode('utf-8')
return Name
def getOverview(self, item):
Temp = item.get("Overview")
if Temp == None:
Temp=''
Overview1=Temp.encode('utf-8')
Overview=str(Overview1)
return Overview
def getPremiereDate(self, item):
if(item.get("PremiereDate") != None):
premieredatelist = (item.get("PremiereDate")).split("T")
premieredate = premieredatelist[0]
else:
premieredate = ""
Temp = premieredate
premieredate = Temp.encode('utf-8')
return premieredate
def getTVInfo(self, item, userData):
TotalSeasons = 0 if item.get("ChildCount")==None else item.get("ChildCount")
TotalEpisodes = 0 if item.get("RecursiveItemCount")==None else item.get("RecursiveItemCount")
WatchedEpisodes = 0 if userData.get("UnplayedItemCount")==None else TotalEpisodes-int(userData.get("UnplayedItemCount"))
UnWatchedEpisodes = 0 if userData.get("UnplayedItemCount")==None else int(userData.get("UnplayedItemCount"))
NumEpisodes = TotalEpisodes
tempEpisode = ""
if (item.get("IndexNumber") != None):
episodeNum = item.get("IndexNumber")
if episodeNum < 10:
tempEpisode = "0" + str(episodeNum)
else:
tempEpisode = str(episodeNum)
tempSeason = ""
if (str(item.get("ParentIndexNumber")) != None):
tempSeason = str(item.get("ParentIndexNumber"))
if item.get("ParentIndexNumber") < 10:
tempSeason = "0" + tempSeason
if item.get("SeriesName") != None:
temp=item.get("SeriesName")
SeriesName=temp.encode('utf-8')
else:
SeriesName=''
return {'TotalSeasons' : str(TotalSeasons),
'TotalEpisodes' : str(TotalEpisodes),
'WatchedEpisodes' : str(WatchedEpisodes),
'UnWatchedEpisodes': str(UnWatchedEpisodes),
'NumEpisodes' : str(NumEpisodes),
'Season' : tempSeason,
'Episode' : tempEpisode,
'SeriesName' : SeriesName
}
def getDate(self, item):
tempDate = item.get("DateCreated")
if tempDate != None:
tempDate = tempDate.split("T")[0]
date = tempDate.split("-")
tempDate = date[2] + "." + date[1] + "." +date[0]
else:
tempDate = "01.01.2000"
return tempDate

View File

@ -0,0 +1,52 @@
from uuid import uuid4 as uuid4
import xbmc
import xbmcaddon
import xbmcgui
class ClientInformation():
def getMachineId(self):
WINDOW = xbmcgui.Window( 10000 )
clientId = WINDOW.getProperty("client_id")
self.addonSettings = xbmcaddon.Addon(id='plugin.video.mb3sync')
if(clientId == None or clientId == ""):
xbmc.log("CLIENT_ID - > No Client ID in WINDOW")
clientId = self.addonSettings.getSetting('client_id')
if(clientId == None or clientId == ""):
xbmc.log("CLIENT_ID - > No Client ID in SETTINGS")
uuid = uuid4()
clientId = str("%012X" % uuid)
WINDOW.setProperty("client_id", clientId)
self.addonSettings.setSetting('client_id', clientId)
xbmc.log("CLIENT_ID - > New Client ID : " + clientId)
else:
WINDOW.setProperty('client_id', clientId)
xbmc.log("CLIENT_ID - > Client ID saved to WINDOW from Settings : " + clientId)
return clientId
def getVersion(self):
version = xbmcaddon.Addon(id="plugin.video.mb3sync").getAddonInfo("version")
return version
def getPlatform(self):
if xbmc.getCondVisibility('system.platform.osx'):
return "OSX"
elif xbmc.getCondVisibility('system.platform.atv2'):
return "ATV2"
elif xbmc.getCondVisibility('system.platform.ios'):
return "iOS"
elif xbmc.getCondVisibility('system.platform.windows'):
return "Windows"
elif xbmc.getCondVisibility('system.platform.linux'):
return "Linux/RPi"
elif xbmc.getCondVisibility('system.platform.android'):
return "Linux/Android"
return "Unknown"

View File

@ -0,0 +1,147 @@
#################################################################################################
# connection manager class
#################################################################################################
import xbmc
import xbmcgui
import xbmcaddon
import json
import threading
from datetime import datetime
from DownloadUtils import DownloadUtils
import urllib
import sys
import socket
#define our global download utils
logLevel = 1
###########################################################################
class ConnectionManager():
addonSettings = None
__addon__ = xbmcaddon.Addon(id='plugin.video.mb3sync')
__addondir__ = xbmc.translatePath( __addon__.getAddonInfo('profile') )
__language__ = __addon__.getLocalizedString
def printDebug(self, msg, level = 1):
if(logLevel >= level):
if(logLevel == 2):
try:
xbmc.log("mb3sync " + str(level) + " -> " + inspect.stack()[1][3] + " : " + str(msg))
except UnicodeEncodeError:
xbmc.log("mb3sync " + str(level) + " -> " + inspect.stack()[1][3] + " : " + str(msg.encode('utf-8')))
else:
try:
xbmc.log("mb3sync " + str(level) + " -> " + str(msg))
except UnicodeEncodeError:
xbmc.log("mb3sync " + str(level) + " -> " + str(msg.encode('utf-8')))
def checkServer(self):
WINDOW = xbmcgui.Window( 10000 )
WINDOW.setProperty("Server_Checked", "True")
self.printDebug ("mb3sync Connection Manager Called")
self.addonSettings = xbmcaddon.Addon(id='plugin.video.mb3sync')
port = self.addonSettings.getSetting('port')
host = self.addonSettings.getSetting('ipaddress')
if(len(host) != 0 and host != "<none>"):
self.printDebug ("mb3sync server already set")
return
serverInfo = self.getServerDetails()
if(serverInfo == None):
self.printDebug ("mb3sync getServerDetails failed")
return
index = serverInfo.find(":")
if(index <= 0):
self.printDebug ("mb3sync getServerDetails data not correct : " + serverInfo)
return
server_address = serverInfo[:index]
server_port = serverInfo[index+1:]
self.printDebug ("mb3sync detected server info " + server_address + " : " + server_port)
xbmcgui.Dialog().ok(self.__language__(30167), self.__language__(30168), self.__language__(30169) + server_address, self.__language__(30030) + server_port)
# get a list of users
self.printDebug ("Getting user list")
jsonData = None
downloadUtils = DownloadUtils()
try:
jsonData = downloadUtils.downloadUrl(server_address + ":" + server_port + "/mediabrowser/Users/Public?format=json")
except Exception, msg:
error = "Get User unable to connect to " + server_address + ":" + server_port + " : " + str(msg)
xbmc.log (error)
return ""
if(jsonData == False):
return
self.printDebug("jsonData : " + str(jsonData), level=2)
result = json.loads(jsonData)
names = []
userList = []
for user in result:
name = user.get("Name")
userList.append(name)
if(user.get("HasPassword") == True):
name = name + " (Secure)"
names.append(name)
self.printDebug ("User List : " + str(names))
self.printDebug ("User List : " + str(userList))
return_value = xbmcgui.Dialog().select(self.__language__(30200), names)
if(return_value > -1):
selected_user = userList[return_value]
self.printDebug("Setting Selected User : " + selected_user)
if self.addonSettings.getSetting("port") != server_port:
self.addonSettings.setSetting("port", server_port)
if self.addonSettings.getSetting("ipaddress") != server_address:
self.addonSettings.setSetting("ipaddress", server_address)
if self.addonSettings.getSetting("username") != selected_user:
self.addonSettings.setSetting("username", selected_user)
def getServerDetails(self):
self.printDebug("Getting Server Details from Network")
MESSAGE = "who is MediaBrowserServer?"
#MULTI_GROUP = ("224.3.29.71", 7359)
#MULTI_GROUP = ("127.0.0.1", 7359)
MULTI_GROUP = ("<broadcast>", 7359)
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.settimeout(6.0)
#ttl = struct.pack('b', 20)
#sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, ttl)
sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 20)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_BROADCAST, 1)
sock.setsockopt(socket.SOL_IP, socket.IP_MULTICAST_LOOP, 1)
sock.setsockopt(socket.IPPROTO_IP, socket.SO_REUSEADDR, 1)
xbmc.log("MutliGroup : " + str(MULTI_GROUP));
xbmc.log("Sending UDP Data : " + MESSAGE);
sock.sendto(MESSAGE, MULTI_GROUP)
try:
data, addr = sock.recvfrom(1024) # buffer size is 1024 bytes
xbmc.log("Received Response : " + data)
if(data[0:18] == "MediaBrowserServer"):
xbmc.log("Found Server : " + data[19:])
return data[19:]
except:
xbmc.log("No UDP Response")
pass
return None

View File

@ -0,0 +1,594 @@
import xbmc
import xbmcgui
import xbmcaddon
import urllib
import urllib2
import httplib
import hashlib
import StringIO
import gzip
import sys
import inspect
import json as json
from random import randrange
from uuid import uuid4 as uuid4
from ClientInformation import ClientInformation
import Utils as utils
import encodings
import time
import traceback
addonSettings = xbmcaddon.Addon(id='plugin.video.mb3sync')
getString = addonSettings.getLocalizedString
class DownloadUtils():
logLevel = 0
getString = None
LogCalls = False
TrackLog = ""
TotalUrlCalls = 0
def __init__(self, *args):
pass
def getServer(self):
port = addonSettings.getSetting('port')
host = addonSettings.getSetting('ipaddress')
return host + ":" + port
def getUserId(self, suppress=True):
WINDOW = xbmcgui.Window( 10000 )
port = addonSettings.getSetting('port')
host = addonSettings.getSetting('ipaddress')
userName = addonSettings.getSetting('username')
userid = WINDOW.getProperty("userid" + userName)
if(userid != None and userid != ""):
utils.logMsg("MB3 Sync","DownloadUtils -> Returning saved UserID : " + userid + "UserName: " + userName)
return userid
utils.logMsg("MB3 Sync","Looking for user name: " + userName)
authOk = self.authenticate()
if(authOk == ""):
if(suppress == False):
xbmcgui.Dialog().ok(getString(30044), getString(30044))
return ""
userid = WINDOW.getProperty("userid"+ userName)
if(userid == "" and suppress == False):
xbmcgui.Dialog().ok(getString(30045),getString(30045))
utils.logMsg("MB3 Sync","userid : " + userid)
self.postcapabilities()
return userid
def postcapabilities(self):
utils.logMsg("MB3 Sync","postcapabilities called")
# Set Capabilities
mb3Port = addonSettings.getSetting('port')
mb3Host = addonSettings.getSetting('ipaddress')
clientInfo = ClientInformation()
machineId = clientInfo.getMachineId()
# get session id
url = "http://" + mb3Host + ":" + mb3Port + "/mediabrowser/Sessions?DeviceId=" + machineId + "&format=json"
utils.logMsg("MB3 Sync","Session URL : " + url);
jsonData = self.downloadUrl(url)
utils.logMsg("MB3 Sync","Session JsonData : " + jsonData)
result = json.loads(jsonData)
utils.logMsg("MB3 Sync","Session JsonData : " + str(result))
sessionId = result[0].get("Id")
utils.logMsg("MB3 Sync","Session Id : " + str(sessionId))
# post capability data
playableMediaTypes = "Audio,Video,Photo"
supportedCommands = "Play,Playstate,DisplayContent,GoHome,SendString,GoToSettings,DisplayMessage,PlayNext"
url = "http://" + mb3Host + ":" + mb3Port + "/mediabrowser/Sessions/Capabilities?Id=" + sessionId + "&PlayableMediaTypes=" + playableMediaTypes + "&SupportedCommands=" + supportedCommands
postData = {}
#postData["Id"] = sessionId;
#postData["PlayableMediaTypes"] = "Video";
#postData["SupportedCommands"] = "MoveUp";
stringdata = json.dumps(postData)
utils.logMsg("MB3 Sync","Capabilities URL : " + url);
utils.logMsg("MB3 Sync","Capabilities Data : " + stringdata)
self.downloadUrl(url, postBody=stringdata, type="POST")
def authenticate(self):
WINDOW = xbmcgui.Window( 10000 )
token = WINDOW.getProperty("AccessToken"+addonSettings.getSetting('username'))
if(token != None and token != ""):
utils.logMsg("MB3 Sync","DownloadUtils -> Returning saved AccessToken for user : " + addonSettings.getSetting('username') + " token: "+ token)
return token
port = addonSettings.getSetting("port")
host = addonSettings.getSetting("ipaddress")
if(host == None or host == "" or port == None or port == ""):
return ""
url = "http://" + addonSettings.getSetting("ipaddress") + ":" + addonSettings.getSetting("port") + "/mediabrowser/Users/AuthenticateByName?format=json"
clientInfo = ClientInformation()
txt_mac = clientInfo.getMachineId()
version = clientInfo.getVersion()
deviceName = addonSettings.getSetting('deviceName')
deviceName = deviceName.replace("\"", "_")
authString = "Mediabrowser Client=\"Kodi\",Device=\"" + deviceName + "\",DeviceId=\"" + txt_mac + "\",Version=\"" + version + "\""
headers = {'Accept-encoding': 'gzip', 'Authorization' : authString}
if addonSettings.getSetting('password') !=None and addonSettings.getSetting('password') !='':
sha1 = hashlib.sha1(addonSettings.getSetting('password'))
sha1 = sha1.hexdigest()
else:
sha1 = 'da39a3ee5e6b4b0d3255bfef95601890afd80709'
messageData = "username=" + addonSettings.getSetting('username') + "&password=" + sha1
resp = self.downloadUrl(url, postBody=messageData, type="POST", authenticate=False, suppress=True)
result = None
accessToken = None
try:
result = json.loads(resp)
accessToken = result.get("AccessToken")
except:
pass
if(result != None and accessToken != None):
utils.logMsg("MB3 Sync","User Authenticated : " + accessToken)
WINDOW.setProperty("AccessToken"+addonSettings.getSetting('username'), accessToken)
WINDOW.setProperty("userid"+addonSettings.getSetting('username'), result.get("User").get("Id"))
WINDOW.setProperty("mb3_authenticated", "true")
return accessToken
else:
utils.logMsg("MB3 Sync","User NOT Authenticated")
WINDOW.setProperty("AccessToken"+addonSettings.getSetting('username'), "")
WINDOW.setProperty("mb3_authenticated", "false")
return ""
def getArtwork(self, data, type, index = "0", userParentInfo = False):
id = data.get("Id")
getSeriesData = False
userData = data.get("UserData")
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 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 != "Primary2" and type != "Primary3" and type != "Primary4" and type != "Thumb" and type != "Banner" and type!="Thumb3":
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" and type != "Primary2" and type != "Primary3" and type != "Primary4":
id = data.get("SeriesId")
getSeriesData = True
if type =="Primary2" or type=="Primary3" or type=="Primary4":
id = data.get("SeasonId")
getSeriesData = True
if data.get("SeasonUserData") != None:
userData = data.get("SeasonUserData")
if id == None:
id=data.get("Id")
imageTag = "e3ab56fe27d389446754d0fb04910a34" # a place holder tag, needs to be in this format
originalType = type
if type == "Primary2" or type == "Primary3" or type == "Primary4" or type=="SeriesPrimary":
type = "Primary"
if type == "Backdrop2" or type=="Backdrop3" or type=="BackdropNoIndicators":
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)
if (data.get("Type") == "Episode" or data.get("Type") == "Season") and type=="Logo":
imageTag = data.get("ParentLogoImageTag")
if (data.get("Type") == "Episode" or data.get("Type") == "Season") and type=="Art":
imageTag = data.get("ParentArtImageTag")
if (data.get("Type") == "Episode") and originalType=="Thumb3":
imageTag = data.get("SeriesThumbImageTag")
if (data.get("Type") == "Season") and originalType=="Thumb3" and imageTag=="e3ab56fe27d389446754d0fb04910a34" :
imageTag = data.get("ParentThumbImageTag")
id = data.get("SeriesId")
query = ""
height = "10000"
width = "10000"
played = "0"
totalbackdrops = 0
if addonSettings.getSetting('showArtIndicators')=='true': # add watched, unplayedcount and percentage played indicators to posters
if (originalType =="Primary" or originalType =="Backdrop" or originalType =="Banner") and data.get("Type") != "Episode":
if originalType =="Backdrop" and index == "0" and data.get("BackdropImageTags") != None:
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 addonSettings.getSetting('showUnplayedIndicators')=='true':
query = query + "&UnplayedCount=" + str(UnWatched)
if(userData != None and userData.get("Played") == True and 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 addonSettings.getSetting('showPlayedPrecentageIndicators')=='true':
played = str(PlayedPercentage)
elif originalType =="Primary2":
if userData != None:
UnWatched = 0 if userData.get("UnplayedItemCount")==None else userData.get("UnplayedItemCount")
if UnWatched <> 0 and addonSettings.getSetting('showUnplayedIndicators')=='true':
query = query + "&UnplayedCount=" + str(UnWatched)
if(userData != None and userData.get("Played") == True and 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 addonSettings.getSetting('showPlayedPrecentageIndicators')=='true':
played = str(PlayedPercentage)
height = "338"
width = "226"
elif originalType =="Primary3" or originalType == "SeriesPrimary":
if userData != None:
UnWatched = 0 if userData.get("UnplayedItemCount")==None else userData.get("UnplayedItemCount")
if UnWatched <> 0 and addonSettings.getSetting('showUnplayedIndicators')=='true':
query = query + "&UnplayedCount=" + str(UnWatched)
if(userData != None and userData.get("Played") == True and 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 addonSettings.getSetting('showPlayedPrecentageIndicators')=='true':
played = str(PlayedPercentage)
elif originalType =="Primary4":
if userData != None:
UnWatched = 0 if userData.get("UnplayedItemCount")==None else userData.get("UnplayedItemCount")
if UnWatched <> 0 and addonSettings.getSetting('showUnplayedIndicators')=='true':
query = query + "&UnplayedCount=" + str(UnWatched)
if(userData != None and userData.get("Played") == True and 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 addonSettings.getSetting('showPlayedPrecentageIndicators')=='true':
played = str(PlayedPercentage)
height = "270"
width = "180"
elif type =="Primary" and data.get("Type") == "Episode":
if userData != None:
UnWatched = 0 if userData.get("UnplayedItemCount")==None else userData.get("UnplayedItemCount")
if UnWatched <> 0 and addonSettings.getSetting('showUnplayedIndicators')=='true':
query = query + "&UnplayedCount=" + str(UnWatched)
if(userData != None and userData.get("Played") == True and 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 addonSettings.getSetting('showPlayedPrecentageIndicators')=='true':
played = str(PlayedPercentage)
height = "410"
width = "770"
elif originalType =="Backdrop2" or originalType =="Thumb2" and data.get("Type") != "Episode":
if originalType =="Backdrop2" and data.get("BackdropImageTags") != None:
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 addonSettings.getSetting('showUnplayedIndicators')=='true':
query = query + "&UnplayedCount=" + str(UnWatched)
if(userData != None and userData.get("Played") == True and 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 addonSettings.getSetting('showPlayedPrecentageIndicators')=='true':
played = str(PlayedPercentage)
height = "370"
width = "660"
elif originalType =="Backdrop3" or originalType =="Thumb3" and data.get("Type") != "Episode":
if originalType =="Backdrop3" and data.get("BackdropImageTags") != None:
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 addonSettings.getSetting('showUnplayedIndicators')=='true':
query = query + "&UnplayedCount=" + str(UnWatched)
if(userData != None and userData.get("Played") == True and 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 addonSettings.getSetting('showPlayedPrecentageIndicators')=='true':
played = str(PlayedPercentage)
height = "910"
width = "1620"
if originalType =="BackdropNoIndicators" and index == "0" and data.get("BackdropImageTags") != None:
totalbackdrops = len(data.get("BackdropImageTags"))
if totalbackdrops != 0:
index = str(randrange(0,totalbackdrops))
# use the local image proxy server that is made available by this addons service
port = addonSettings.getSetting('port')
host = addonSettings.getSetting('ipaddress')
server = host + ":" + port
if addonSettings.getSetting('compressArt')=='true':
query = query + "&Quality=90"
if imageTag == None:
imageTag = "e3ab56fe27d389446754d0fb04910a34"
artwork = "http://" + server + "/mediabrowser/Items/" + str(id) + "/Images/" + type + "/" + index + "/" + imageTag + "/original/" + width + "/" + height + "/" + played + "?" + query
if addonSettings.getSetting('disableCoverArt')=='true':
artwork = artwork + "&EnableImageEnhancers=false"
utils.logMsg("MB3 Sync","getArtwork : " + artwork, level=2)
# do not return non-existing images
if ( (type!="Backdrop" and imageTag=="e3ab56fe27d389446754d0fb04910a34") | #Remember, this is the placeholder tag, meaning we didn't find a valid tag
(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" or (type=="Backdrop" and getSeriesData==True and data.get("ParentBackdropImageTags") == None) or (type=="Backdrop" and getSeriesData!=True):
artwork=''
return artwork
def getUserArtwork(self, data, type, index = "0"):
id = data.get("Id")
port = addonSettings.getSetting('port')
host = addonSettings.getSetting('ipaddress')
server = host + ":" + port
artwork = "http://" + server + "/mediabrowser/Users/" + str(id) + "/Images/" + type + "?Format=original"
return artwork
def imageUrl(self, id, type, index, width, height):
port = addonSettings.getSetting('port')
host = addonSettings.getSetting('ipaddress')
server = host + ":" + port
return "http://" + server + "/mediabrowser/Items/" + str(id) + "/Images/" + type + "/" + str(index) + "/e3ab56fe27d389446754d0fb04910a34/original/" + str(width) + "/" + str(height) + "/0"
def getAuthHeader(self, authenticate=True):
clientInfo = ClientInformation()
txt_mac = clientInfo.getMachineId()
version = clientInfo.getVersion()
deviceName = addonSettings.getSetting('deviceName')
deviceName = deviceName.replace("\"", "_")
if(authenticate == False):
authString = "MediaBrowser Client=\"Kodi\",Device=\"" + deviceName + "\",DeviceId=\"" + txt_mac + "\",Version=\"" + version + "\""
headers = {"Accept-encoding": "gzip", "Accept-Charset" : "UTF-8,*", "Authorization" : authString}
return headers
else:
userid = self.getUserId()
authString = "MediaBrowser UserId=\"" + userid + "\",Client=\"Kodi\",Device=\"" + deviceName + "\",DeviceId=\"" + txt_mac + "\",Version=\"" + version + "\""
headers = {"Accept-encoding": "gzip", "Accept-Charset" : "UTF-8,*", "Authorization" : authString}
authToken = self.authenticate()
if(authToken != ""):
headers["X-MediaBrowser-Token"] = authToken
utils.logMsg("MB3 Sync","Authentication Header : " + str(headers))
return headers
def downloadUrl(self, url, suppress=False, postBody=None, type="GET", popup=0, authenticate=True ):
utils.logMsg("MB3 Sync","== ENTER: getURL ==")
self.TotalUrlCalls = self.TotalUrlCalls + 1
if(self.LogCalls):
stackString = ""
for f in inspect.stack():
stackString = stackString + "\r - " + str(f)
self.TrackLog = self.TrackLog + "HTTP_API_CALL : " + url + stackString + "\r"
link = ""
try:
if url[0:4] == "http":
serversplit = 2
urlsplit = 3
else:
serversplit = 0
urlsplit = 1
server = url.split('/')[serversplit]
urlPath = "/"+"/".join(url.split('/')[urlsplit:])
utils.logMsg("MB3 Sync","DOWNLOAD_URL = " + url)
utils.logMsg("MB3 Sync","server = "+str(server), level=2)
utils.logMsg("MB3 Sync","urlPath = "+str(urlPath), level=2)
conn = httplib.HTTPConnection(server, timeout=5)
head = self.getAuthHeader(authenticate)
utils.logMsg("MB3 Sync","HEADERS : " + str(head), level=1)
# make the connection and send the request
if(postBody != None):
head["Content-Type"] = "application/x-www-form-urlencoded"
head["Content-Length"] = str(len(postBody))
utils.logMsg("MB3 Sync","POST DATA : " + postBody)
conn.request(method=type, url=urlPath, body=postBody, headers=head)
else:
conn.request(method=type, url=urlPath, headers=head)
# get the response
tries = 0
while tries <= 4:
try:
data = conn.getresponse()
break
except:
# TODO: we need to work out which errors we can just quit trying immediately
if(xbmc.abortRequested == True):
return ""
xbmc.sleep(100)
if(xbmc.abortRequested == True):
return ""
tries += 1
if tries == 5:
data = conn.getresponse()
utils.logMsg("MB3 Sync","GET URL HEADERS : " + str(data.getheaders()), level=2)
# process the response
contentType = "none"
if int(data.status) == 200:
retData = data.read()
contentType = data.getheader('content-encoding')
utils.logMsg("MB3 Sync","Data Len Before : " + str(len(retData)), level=2)
if(contentType == "gzip"):
retData = StringIO.StringIO(retData)
gzipper = gzip.GzipFile(fileobj=retData)
link = gzipper.read()
else:
link = retData
utils.logMsg("MB3 Sync","Data Len After : " + str(len(link)), level=2)
utils.logMsg("MB3 Sync","====== 200 returned =======", level=2)
utils.logMsg("MB3 Sync","Content-Type : " + str(contentType), level=2)
utils.logMsg("MB3 Sync",link, level=2)
utils.logMsg("MB3 Sync","====== 200 finished ======", level=2)
elif ( int(data.status) == 301 ) or ( int(data.status) == 302 ):
try:
conn.close()
except:
pass
return data.getheader('Location')
elif int(data.status) == 401:
error = "HTTP response error: " + str(data.status) + " " + str(data.reason)
xbmc.log(error)
WINDOW = xbmcgui.Window(10000)
timeStamp = WINDOW.getProperty("mb3sync_LAST_USER_ERROR")
if(timeStamp == None or timeStamp == ""):
timeStamp = "0"
if((int(timeStamp) + 10) < int(time.time())):
xbmcgui.Dialog().ok(getString(30135), getString(30044))
WINDOW.setProperty("mb3sync_LAST_USER_ERROR", str(int(time.time())))
try:
conn.close()
except:
pass
return ""
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(getString(30135),server)
try:
conn.close()
except:
pass
return ""
else:
link = ""
except Exception, msg:
error = "Unable to connect to " + str(server) + " : " + str(msg)
xbmc.log(error)
stack = self.FormatException()
utils.logMsg("MB3 Sync",stack)
if suppress is False:
if popup == 0:
xbmc.executebuiltin("XBMC.Notification(: Connection Error: Error connecting to server,)")
else:
xbmcgui.Dialog().ok(getString(30204), str(msg))
pass
else:
try:
conn.close()
except:
pass
return link
def FormatException(self):
exception_list = traceback.format_stack()
exception_list = exception_list[:-2]
exception_list.extend(traceback.format_tb(sys.exc_info()[2]))
exception_list.extend(traceback.format_exception_only(sys.exc_info()[0], sys.exc_info()[1]))
exception_str = "Traceback (most recent call last):\n"
exception_str += "".join(exception_list)
# Removing the last \n
exception_str = exception_str[:-1]
return exception_str
def __del__(self):
return
# xbmc.log("\rURL_REQUEST_REPORT : Total Calls : " + str(self.TotalUrlCalls) + "\r" + self.TrackLog)

View File

@ -0,0 +1,40 @@
#################################################################################################
# Kodi Monitor
# Watched events that occur in Kodi, like setting media watched
#################################################################################################
import xbmc
import xbmcgui
import xbmcaddon
import json
import Utils as utils
from LibrarySync import LibrarySync
librarySync = LibrarySync()
WINDOW = xbmcgui.Window( 10000 )
class Kodi_Monitor(xbmc.Monitor):
def __init__(self, *args, **kwargs):
xbmc.Monitor.__init__(self)
def onDatabaseUpdated(self, database):
pass
#this library monitor is used to detect a watchedstate change by the user through the library
def onNotification (self,sender,method,data):
if method == "VideoLibrary.OnUpdate":
#check windowprop if the sync is busy to prevent any false updates
if WINDOW.getProperty("librarysync") != "busy":
jsondata = json.loads(data)
if jsondata != None:
playcount = None
playcount = jsondata.get("playcount")
item = jsondata.get("item").get("id")
if playcount != None:
librarySync.updatePlayCountFromKodi(item, playcount)

View File

@ -0,0 +1,270 @@
#################################################################################################
# LibrarySync
#################################################################################################
import xbmc
import xbmcgui
import xbmcaddon
import xbmcvfs
import json
import threading
import urllib
from datetime import datetime, timedelta, time
import urllib2
import os
from xml.etree.ElementTree import Element, SubElement, Comment, tostring
from xml.etree import ElementTree
from xml.dom import minidom
import xml.etree.cElementTree as ET
from API import API
import Utils as utils
from DownloadUtils import DownloadUtils
downloadUtils = DownloadUtils()
addon = xbmcaddon.Addon(id='plugin.video.mb3sync')
addondir = xbmc.translatePath( addon.getAddonInfo('profile') )
dataPath = os.path.join(addondir,"library")
movieLibrary = os.path.join(dataPath,'movies')
tvLibrary = os.path.join(dataPath,'tvshows')
WINDOW = xbmcgui.Window( 10000 )
port = addon.getSetting('port')
host = addon.getSetting('ipaddress')
server = host + ":" + port
userid = downloadUtils.getUserId()
class LibrarySync():
def syncDatabase(self):
WINDOW.setProperty("librarysync", "busy")
updateNeeded = False
allMovies = list()
for item in self.getMovies(True):
if not item.get('IsFolder'):
kodiItem = self.getKodiMovie(item["Id"])
allMovies.append(item["Id"])
if kodiItem == None:
self.addMovieToKodiLibrary(item)
updateNeeded = True
else:
self.updateMovieToKodiLibrary(item, kodiItem)
cleanNeeded = False
# process deletes
allLocaldirs, filesMovies = xbmcvfs.listdir(movieLibrary)
allMB3Movies = set(allMovies)
for dir in allLocaldirs:
if not dir in allMB3Movies:
self.deleteMovieFromKodiLibrary(dir)
cleanneeded = True
if cleanNeeded:
xbmc.executebuiltin("CleanLibrary(video)")
if updateNeeded:
xbmc.executebuiltin("UpdateLibrary(video)")
WINDOW.clearProperty("librarysync")
def updatePlayCounts(self):
#update all playcounts from MB3 to Kodi library
WINDOW.setProperty("librarysync", "busy")
for item in self.getMovies(False):
if not item.get('IsFolder'):
kodiItem = self.getKodiMovie(item["Id"])
userData=API().getUserData(item)
timeInfo = API().getTimeInfo(item)
if kodiItem != None:
if kodiItem['playcount'] != int(userData.get("PlayCount")):
xbmc.executeJSONRPC('{"jsonrpc": "2.0", "method": "VideoLibrary.SetMovieDetails", "params": { "movieid": %i, "playcount": %i}, "id": 1 }' %(kodiItem['movieid'], int(userData.get("PlayCount"))))
if kodiItem['playcount'] != int(userData.get("PlayCount")):
xbmc.executeJSONRPC('{"jsonrpc": "2.0", "method": "VideoLibrary.SetMovieDetails", "params": { "movieid": %i, "playcount": %i}, "id": 1 }' %(kodiItem['movieid'], int(userData.get("PlayCount"))))
WINDOW.clearProperty("librarysync")
def getMovies(self, fullinfo = False):
result = None
if fullinfo:
url = server + '/mediabrowser/Users/' + userid + '/Items?&SortBy=SortName&Fields=Path,Genres,Studios,CumulativeRunTimeTicks,Metascore,AirTime,DateCreated,MediaStreams,People,Overview&Recursive=true&SortOrder=Ascending&IncludeItemTypes=Movie&format=json&ImageTypeLimit=1'
else:
url = server + '/mediabrowser/Users/' + userid + '/Items?&SortBy=SortName&Fields=CumulativeRunTimeTicks&Recursive=true&SortOrder=Ascending&IncludeItemTypes=Movie&format=json&ImageTypeLimit=1'
jsonData = downloadUtils.downloadUrl(url, suppress=True, popup=0)
if jsonData != None:
result = json.loads(jsonData)
if(result.has_key('Items')):
result = result['Items']
return result
def updatePlayCountFromKodi(self, id, playcount=0):
#when user marks item watched from kodi interface update this to MB3
json_response = xbmc.executeJSONRPC('{"jsonrpc": "2.0", "method": "VideoLibrary.GetMovieDetails", "params": { "movieid": ' + str(id) + ', "properties" : ["playcount", "file"] }, "id": "1"}')
if json_response != None:
jsonobject = json.loads(json_response.decode('utf-8','replace'))
movie = None
if(jsonobject.has_key('result')):
result = jsonobject['result']
if(result.has_key('moviedetails')):
moviedetails = result['moviedetails']
filename = moviedetails.get("file").rpartition('\\')[2]
mb3Id = filename.replace(".strm","")
watchedurl = 'http://' + host + ':' + port + '/mediabrowser/Users/' + userid + '/PlayedItems/' + mb3Id
print "watchedurl -->" + watchedurl
if playcount != 0:
downloadUtils.downloadUrl(watchedurl, postBody="", type="POST")
else:
downloadUtils.downloadUrl(watchedurl, type="DELETE")
def updateMovieToKodiLibrary( self, MBitem, KodiItem ):
#TODO: only update the info if something is actually changed
timeInfo = API().getTimeInfo(MBitem)
userData=API().getUserData(MBitem)
people = API().getPeople(MBitem)
mediaStreams=API().getMediaStreams(MBitem)
thumbPath = downloadUtils.getArtwork(MBitem, "Primary")
utils.logMsg("Updating item to Kodi Library", MBitem["Id"] + " - " + MBitem["Name"])
#update artwork
self.updateArtWork(KodiItem,"poster", downloadUtils.getArtwork(MBitem, "poster"))
self.updateArtWork(KodiItem,"clearlogo", downloadUtils.getArtwork(MBitem, "Logo"))
self.updateArtWork(KodiItem,"banner", downloadUtils.getArtwork(MBitem, "Banner"))
self.updateArtWork(KodiItem,"landscape", downloadUtils.getArtwork(MBitem, "Thumb"))
self.updateArtWork(KodiItem,"discart", downloadUtils.getArtwork(MBitem, "Disc"))
self.updateArtWork(KodiItem,"fanart", downloadUtils.getArtwork(MBitem, "Backdrop"))
#update duration
duration = (int(timeInfo.get('Duration'))*60)
if KodiItem['runtime'] != duration:
xbmc.executeJSONRPC('{"jsonrpc": "2.0", "method": "VideoLibrary.SetMovieDetails", "params": { "movieid": %i, "runtime": %s}, "id": 1 }' %(KodiItem['movieid'], duration))
#update year
if KodiItem['year'] != MBitem.get("ProductionYear") and MBitem.get("ProductionYear") != None:
xbmc.executeJSONRPC('{"jsonrpc": "2.0", "method": "VideoLibrary.SetMovieDetails", "params": { "movieid": %i, "year": %s}, "id": 1 }' %(KodiItem['movieid'], MBitem.get("ProductionYear")))
#update strm file - TODO: only update strm when path has changed
self.createSTRM(MBitem["Id"])
#update nfo file - needed for testing
nfoFile = os.path.join(movieLibrary,MBitem["Id"],MBitem["Id"] + ".nfo")
if not xbmcvfs.exists(nfoFile):
self.createNFO(MBitem)
#update playcounts
if KodiItem['playcount'] != int(userData.get("PlayCount")):
xbmc.executeJSONRPC('{"jsonrpc": "2.0", "method": "VideoLibrary.SetMovieDetails", "params": { "movieid": %i, "playcount": %i}, "id": 1 }' %(KodiItem['movieid'], int(userData.get("PlayCount"))))
def updateArtWork(self,KodiItem,artWorkName,artworkValue):
if KodiItem['art'].has_key(artWorkName):
if KodiItem['art'][artWorkName] != artworkValue and artworkValue != None:
xbmc.executeJSONRPC('{"jsonrpc": "2.0", "method": "VideoLibrary.SetMovieDetails", "params": { "movieid": %i, "art": { "%s": "%s" }}, "id": 1 }' %(KodiItem['movieid'], artWorkName, artworkValue))
def createSTRM(self,id):
itemPath = os.path.join(movieLibrary,id)
if not xbmcvfs.exists(itemPath):
xbmcvfs.mkdir(itemPath)
strmFile = os.path.join(itemPath,id + ".strm")
text_file = open(strmFile, "w")
playUrl = "plugin://plugin.video.mb3sync/?id=" + id + '&mode=play'
text_file.writelines(playUrl)
text_file.close()
def createNFO(self,item):
timeInfo = API().getTimeInfo(item)
userData=API().getUserData(item)
people = API().getPeople(item)
mediaStreams=API().getMediaStreams(item)
#todo: change path if type is not movie
itemPath = os.path.join(movieLibrary,item["Id"])
nfoFile = os.path.join(itemPath,item["Id"] + ".nfo")
root = Element("movie")
SubElement(root, "id").text = item["Id"]
SubElement(root, "tag").text = item["Id"]
SubElement(root, "thumb").text = downloadUtils.getArtwork(item, "poster")
SubElement(root, "fanart").text = timeInfo.get('Backdrop')
SubElement(root, "title").text = item["Name"].encode('utf-8').decode('utf-8')
SubElement(root, "originaltitle").text = item["Id"]
SubElement(root, "year").text = str(item.get("ProductionYear"))
SubElement(root, "runtime").text = str(timeInfo.get('Duration'))
fileinfo = SubElement(root, "fileinfo")
streamdetails = SubElement(fileinfo, "streamdetails")
video = SubElement(streamdetails, "video")
SubElement(video, "duration").text = str(timeInfo.get('totaltime'))
SubElement(video, "aspect").text = timeInfo.get('aspectratio')
SubElement(video, "codec").text = timeInfo.get('videocodec')
SubElement(video, "width").text = str(timeInfo.get('width'))
SubElement(video, "height").text = str(timeInfo.get('height'))
audio = SubElement(streamdetails, "audio")
SubElement(audio, "codec").text = timeInfo.get('audiocodec')
SubElement(audio, "channels").text = timeInfo.get('channels')
SubElement(root, "plot").text = API().getOverview(item).decode('utf-8')
art = SubElement(root, "art")
SubElement(art, "poster").text = downloadUtils.getArtwork(item, "poster")
SubElement(art, "fanart").text = downloadUtils.getArtwork(item, "Backdrop")
SubElement(art, "landscape").text = downloadUtils.getArtwork(item, "Thumb")
SubElement(art, "clearlogo").text = downloadUtils.getArtwork(item, "Logo")
SubElement(art, "discart").text = downloadUtils.getArtwork(item, "Disc")
SubElement(art, "banner").text = downloadUtils.getArtwork(item, "Banner")
ET.ElementTree(root).write(nfoFile, encoding="utf-8", xml_declaration=True)
def addMovieToKodiLibrary( self, item ):
itemPath = os.path.join(movieLibrary,item["Id"])
strmFile = os.path.join(itemPath,item["Id"] + ".strm")
utils.logMsg("Adding item to Kodi Library",item["Id"] + " - " + item["Name"])
#create path if not exists
if not xbmcvfs.exists(itemPath):
xbmcvfs.mkdir(itemPath)
#create nfo file
self.createNFO(item)
# create strm file
self.createSTRM(item["Id"])
def deleteMovieFromKodiLibrary(self, id ):
kodiItem = self.getKodiMovie(id)
utils.logMsg("deleting movie from Kodi library",id)
if kodiItem != None:
xbmc.executeJSONRPC('{"jsonrpc": "2.0", "method": "VideoLibrary.RemoveMovie", "params": { "movieid": %i}, "id": 1 }' %(kodiItem["movieid"]))
path = os.path.join(movieLibrary,id)
xbmcvfs.rmdir(path)
def getKodiMovie(self, id):
json_response = xbmc.executeJSONRPC('{"jsonrpc": "2.0", "method": "VideoLibrary.GetMovies", "params": { "filter": {"operator": "contains", "field": "path", "value": "' + id + '"}, "properties" : ["art", "rating", "thumbnail", "runtime", "year", "plot", "playcount", "file"], "sort": { "order": "ascending", "method": "label", "ignorearticle": true } }, "id": "libMovies"}')
jsonobject = json.loads(json_response.decode('utf-8','replace'))
movie = None
if(jsonobject.has_key('result')):
result = jsonobject['result']
if(result.has_key('movies')):
movies = result['movies']
movie = movies[0]
return movie

170
resources/lib/PlayUtils.py Normal file
View File

@ -0,0 +1,170 @@
#################################################################################################
# utils class
#################################################################################################
import xbmc
import xbmcgui
import xbmcaddon
import json
import threading
from datetime import datetime
from DownloadUtils import DownloadUtils
from ClientInformation import ClientInformation
import urllib
import sys
import os
#define our global download utils
downloadUtils = DownloadUtils()
clientInfo = ClientInformation()
###########################################################################
class PlayUtils():
def getPlayUrl(self, server, id, result):
addonSettings = xbmcaddon.Addon(id='plugin.video.mb3sync')
# if the path is local and depending on the video quality play we can direct play it do so-
if self.isDirectPlay(result) == True:
xbmc.log("mb3sync 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'))
else:
#No path or has a path but not sufficient network so transcode
xbmc.log("mb3sync getPlayUrl -> Transcode")
if result.get("Type") == "Audio":
playurl = 'http://' + server + '/mediabrowser/Audio/' + id + '/stream.mp3'
else:
txt_mac = clientInfo.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('DefaultSubtitleStreamIndex'))
return playurl.encode('utf-8')
# Works out if we are direct playing or not
def isDirectPlay(self, result):
if (self.fileExists(result) or (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("mb3sync isNetworkQualitySufficient -> FALSE bit rate - settingsVideoBitRate: " + str(settingsVideoBitRate) + " mediasource bitrate: " + str(mediaSources[0].get('Bitrate')))
return False
else:
xbmc.log("mb3sync isNetworkQualitySufficient -> TRUE bit rate")
return True
# Any thing else is ok
xbmc.log("mb3sync isNetworkQualitySufficient -> TRUE default")
return True
# get the addon video quality
def getVideoBitRate(self):
addonSettings = xbmcaddon.Addon(id='plugin.video.mb3sync')
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'
def fileExists(self, result):
path=result.get("Path").encode('utf-8')
if os.path.exists(path) == True:
return True
else:
return False
# Works out if the network quality can play directly or if transcoding is needed
def isLocalPath(self, result):
path=result.get("Path").encode('utf-8')
playurl = 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,180 @@
import xbmc
import xbmcplugin
import xbmcgui
import xbmcaddon
import urllib
import datetime
import time
import json as json
import inspect
import sys
from DownloadUtils import DownloadUtils
downloadUtils = DownloadUtils()
from PlayUtils import PlayUtils
from API import API
import Utils as utils
addon = xbmcaddon.Addon(id='plugin.video.mb3sync')
language = addon.getLocalizedString
WINDOW = xbmcgui.Window( 10000 )
port = addon.getSetting('port')
host = addon.getSetting('ipaddress')
server = host + ":" + port
userid = downloadUtils.getUserId()
class PlaybackUtils():
settings = None
language = None
logLevel = 0
def __init__(self, *args):
pass
def PLAY(self, id):
jsonData = downloadUtils.downloadUrl("http://" + server + "/mediabrowser/Users/" + userid + "/Items/" + id + "?format=json&ImageTypeLimit=1", suppress=False, popup=1 )
result = json.loads(jsonData)
userData = result.get("UserData")
resume_result = 0
seekTime = 0
if userData.get("PlaybackPositionTicks") != 0:
reasonableTicks = int(userData.get("PlaybackPositionTicks")) / 1000
seekTime = reasonableTicks / 10000
displayTime = str(datetime.timedelta(seconds=seekTime))
display_list = [ language(30106) + ' ' + displayTime, language(30107)]
resumeScreen = xbmcgui.Dialog()
resume_result = resumeScreen.select(language(30105), display_list)
playurl = PlayUtils().getPlayUrl(server, id, result)
xbmc.log("Play URL: " + playurl)
thumbPath = downloadUtils.getArtwork(result, "Primary")
listItem = xbmcgui.ListItem(path=playurl, iconImage=thumbPath, thumbnailImage=thumbPath)
self.setListItemProps(server, id, listItem, result)
# Can not play virtual items
if (result.get("LocationType") == "Virtual"):
xbmcgui.Dialog().ok(self.language(30128), language(30129))
watchedurl = 'http://' + server + '/mediabrowser/Users/'+ userid + '/PlayedItems/' + id
positionurl = 'http://' + server + '/mediabrowser/Users/'+ userid + '/PlayingItems/' + id
deleteurl = 'http://' + server + '/mediabrowser/Items/' + id
# set the current playing info
WINDOW.setProperty(playurl+"watchedurl", watchedurl)
WINDOW.setProperty(playurl+"positionurl", positionurl)
WINDOW.setProperty(playurl+"deleteurl", "")
WINDOW.setProperty(playurl+"deleteurl", deleteurl)
if resume_result == 0:
WINDOW.setProperty(playurl+"seektime", str(seekTime))
else:
WINDOW.clearProperty(playurl+"seektime")
if result.get("Type")=="Episode":
WINDOW.setProperty(playurl+"refresh_id", result.get("SeriesId"))
else:
WINDOW.setProperty(playurl+"refresh_id", id)
WINDOW.setProperty(playurl+"runtimeticks", str(result.get("RunTimeTicks")))
WINDOW.setProperty(playurl+"type", result.get("Type"))
WINDOW.setProperty(playurl+"item_id", id)
if PlayUtils().isDirectPlay(result) == True:
playMethod = "DirectPlay"
else:
playMethod = "Transcode"
WINDOW.setProperty(playurl+"playmethod", playMethod)
mediaSources = result.get("MediaSources")
if(mediaSources != None):
if mediaSources[0].get('DefaultAudioStreamIndex') != None:
WINDOW.setProperty(playurl+"AudioStreamIndex", str(mediaSources[0].get('DefaultAudioStreamIndex')))
if mediaSources[0].get('DefaultSubtitleStreamIndex') != None:
WINDOW.setProperty(playurl+"SubtitleStreamIndex", str(mediaSources[0].get('DefaultSubtitleStreamIndex')))
#this launches the playback
xbmcplugin.setResolvedUrl(int(sys.argv[1]), True, listItem)
def setArt(self, list,name,path):
if name=='thumb' or name=='fanart_image' or name=='small_poster' or name=='tiny_poster' or name == "medium_landscape" or name=='medium_poster' or name=='small_fanartimage' or name=='medium_fanartimage' or name=='fanart_noindicators':
list.setProperty(name, path)
else:
list.setArt({name:path})
return list
def setListItemProps(self, server, id, listItem, result):
# set up item and item info
userid = downloadUtils.getUserId()
thumbID = id
eppNum = -1
seasonNum = -1
tvshowTitle = ""
if(result.get("Type") == "Episode"):
thumbID = result.get("SeriesId")
seasonNum = result.get("ParentIndexNumber")
eppNum = result.get("IndexNumber")
tvshowTitle = result.get("SeriesName")
self.setArt(listItem,'poster', downloadUtils.getArtwork(result, "Primary"))
self.setArt(listItem,'tvshow.poster', downloadUtils.getArtwork(result, "SeriesPrimary"))
self.setArt(listItem,'clearart', downloadUtils.getArtwork(result, "Art"))
self.setArt(listItem,'tvshow.clearart', downloadUtils.getArtwork(result, "Art"))
self.setArt(listItem,'clearlogo', downloadUtils.getArtwork(result, "Logo"))
self.setArt(listItem,'tvshow.clearlogo', downloadUtils.getArtwork(result, "Logo"))
self.setArt(listItem,'discart', downloadUtils.getArtwork(result, "Disc"))
self.setArt(listItem,'fanart_image', downloadUtils.getArtwork(result, "Backdrop"))
self.setArt(listItem,'landscape', downloadUtils.getArtwork(result, "Thumb"))
listItem.setProperty('IsPlayable', 'true')
listItem.setProperty('IsFolder', 'false')
# Process Studios
studio = API().getStudio(result)
listItem.setInfo('video', {'studio' : studio})
# play info
playinformation = ''
if PlayUtils().isDirectPlay(result) == True:
playinformation = language(30165)
else:
playinformation = language(30166)
details = {
'title' : result.get("Name", "Missing Name") + ' - ' + playinformation,
'plot' : result.get("Overview")
}
if(eppNum > -1):
details["episode"] = str(eppNum)
if(seasonNum > -1):
details["season"] = str(seasonNum)
if tvshowTitle != None:
details["TVShowTitle"] = tvshowTitle
listItem.setInfo( "Video", infoLabels=details )
people = API().getPeople(result)
# Process Genres
genre = API().getGenre(result)
listItem.setInfo('video', {'director' : people.get('Director')})
listItem.setInfo('video', {'writer' : people.get('Writer')})
listItem.setInfo('video', {'mpaa': result.get("OfficialRating")})
listItem.setInfo('video', {'genre': genre})

313
resources/lib/Player.py Normal file
View File

@ -0,0 +1,313 @@
import xbmcaddon
import xbmcplugin
import xbmc
import xbmcgui
import os
import threading
import json
import KodiMonitor
import Utils as utils
from DownloadUtils import DownloadUtils
from PlayUtils import PlayUtils
from ClientInformation import ClientInformation
from LibrarySync import LibrarySync
librarySync = LibrarySync()
# service class for playback monitoring
class Player( xbmc.Player ):
logLevel = 0
played_information = {}
downloadUtils = None
settings = None
playStats = {}
def __init__( self, *args ):
self.settings = xbmcaddon.Addon(id='plugin.video.mb3sync')
self.downloadUtils = DownloadUtils()
try:
self.logLevel = int(self.settings.getSetting('logLevel'))
except:
pass
self.printDebug("mb3sync Service -> starting playback monitor service")
self.played_information = {}
pass
def printDebug(self, msg, level = 1):
if(self.logLevel >= level):
if(self.logLevel == 2):
try:
xbmc.log("mb3sync " + str(level) + " -> " + inspect.stack()[1][3] + " : " + str(msg))
except UnicodeEncodeError:
xbmc.log("mb3sync " + str(level) + " -> " + inspect.stack()[1][3] + " : " + str(msg.encode('utf-8')))
else:
try:
xbmc.log("mb3sync " + str(level) + " -> " + str(msg))
except UnicodeEncodeError:
xbmc.log("mb3sync " + str(level) + " -> " + str(msg.encode('utf-8')))
def deleteItem (self, url):
return_value = xbmcgui.Dialog().yesno(__language__(30091),__language__(30092))
if return_value:
self.printDebug('Deleting via URL: ' + url)
progress = xbmcgui.DialogProgress()
progress.create(__language__(30052), __language__(30053))
self.downloadUtils.downloadUrl(url, type="DELETE")
progress.close()
xbmc.executebuiltin("Container.Refresh")
return 1
else:
return 0
def hasData(self, data):
if(data == None or len(data) == 0 or data == "None"):
return False
else:
return True
def stopAll(self):
if(len(self.played_information) == 0):
return
addonSettings = xbmcaddon.Addon(id='plugin.video.mb3sync')
self.printDebug("mb3sync Service -> played_information : " + str(self.played_information))
for item_url in self.played_information:
data = self.played_information.get(item_url)
if(data != None):
self.printDebug("mb3sync Service -> item_url : " + item_url)
self.printDebug("mb3sync Service -> item_data : " + str(data))
deleteurl = data.get("deleteurl")
runtime = data.get("runtime")
currentPosition = data.get("currentPosition")
item_id = data.get("item_id")
refresh_id = data.get("refresh_id")
currentFile = data.get("currentfile")
if(refresh_id != None):
#todo: trigger update of single item from MB3, for now trigger full playcounts update
librarySync.updatePlayCounts()
if(currentPosition != None and self.hasData(runtime)):
runtimeTicks = int(runtime)
self.printDebug("mb3sync Service -> runtimeticks:" + str(runtimeTicks))
percentComplete = (currentPosition * 10000000) / runtimeTicks
markPlayedAt = float(90) / 100
self.printDebug("mb3sync Service -> Percent Complete:" + str(percentComplete) + " Mark Played At:" + str(markPlayedAt))
self.stopPlayback(data)
if (percentComplete > markPlayedAt):
gotDeleted = 0
if(deleteurl != None and deleteurl != ""):
self.printDebug("mb3sync Service -> Offering Delete:" + str(deleteurl))
gotDeleted = self.deleteItem(deleteurl)
self.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
self.downloadUtils.downloadUrl(url, type="DELETE")
def stopPlayback(self, data):
addonSettings = xbmcaddon.Addon(id='plugin.video.mb3sync')
item_id = data.get("item_id")
audioindex = data.get("AudioStreamIndex")
subtitleindex = data.get("SubtitleStreamIndex")
playMethod = data.get("playmethod")
currentPosition = data.get("currentPosition")
positionTicks = str(int(currentPosition * 10000000))
url = ("http://%s:%s/mediabrowser/Sessions/Playing/Stopped" % (addonSettings.getSetting('ipaddress'), addonSettings.getSetting('port')))
url = url + "?itemId=" + item_id
url = url + "&canSeek=true"
url = url + "&PlayMethod=" + playMethod
url = url + "&QueueableMediaTypes=Video"
url = url + "&MediaSourceId=" + item_id
url = url + "&PositionTicks=" + positionTicks
if(audioindex != None and audioindex!=""):
url = url + "&AudioStreamIndex=" + audioindex
if(subtitleindex != None and subtitleindex!=""):
url = url + "&SubtitleStreamIndex=" + subtitleindex
self.downloadUtils.downloadUrl(url, postBody="", type="POST")
def reportPlayback(self):
self.printDebug("reportPlayback Called")
currentFile = xbmc.Player().getPlayingFile()
#TODO need to change this to use the one in the data map
playTime = xbmc.Player().getTime()
data = self.played_information.get(currentFile)
# only report playback if mb3sync has initiated the playback (item_id has value)
if(data != None and data.get("item_id") != None):
addonSettings = xbmcaddon.Addon(id='plugin.video.mb3sync')
item_id = data.get("item_id")
audioindex = data.get("AudioStreamIndex")
subtitleindex = data.get("SubtitleStreamIndex")
playMethod = data.get("playmethod")
paused = data.get("paused")
url = ("http://%s:%s/mediabrowser/Sessions/Playing/Progress" % (addonSettings.getSetting('ipaddress'), addonSettings.getSetting('port')))
url = url + "?itemId=" + item_id
url = url + "&canSeek=true"
url = url + "&PlayMethod=" + playMethod
url = url + "&QueueableMediaTypes=Video"
url = url + "&MediaSourceId=" + item_id
url = url + "&PositionTicks=" + str(int(playTime * 10000000))
if(audioindex != None and audioindex!=""):
url = url + "&AudioStreamIndex=" + audioindex
if(subtitleindex != None and subtitleindex!=""):
url = url + "&SubtitleStreamIndex=" + subtitleindex
if(paused == None):
paused = "false"
url = url + "&IsPaused=" + paused
self.downloadUtils.downloadUrl(url, postBody="", type="POST")
def onPlayBackPaused( self ):
currentFile = xbmc.Player().getPlayingFile()
self.printDebug("PLAYBACK_PAUSED : " + currentFile)
if(self.played_information.get(currentFile) != None):
self.played_information[currentFile]["paused"] = "true"
self.reportPlayback()
def onPlayBackResumed( self ):
currentFile = xbmc.Player().getPlayingFile()
self.printDebug("PLAYBACK_RESUMED : " + currentFile)
if(self.played_information.get(currentFile) != None):
self.played_information[currentFile]["paused"] = "false"
self.reportPlayback()
def onPlayBackSeek( self, time, seekOffset ):
self.printDebug("PLAYBACK_SEEK")
self.reportPlayback()
def onPlayBackStarted( self ):
# Will be called when xbmc starts playing a file
WINDOW = xbmcgui.Window( 10000 )
self.stopAll()
addonSettings = xbmcaddon.Addon(id='plugin.video.mb3sync')
if xbmc.Player().isPlaying():
currentFile = xbmc.Player().getPlayingFile()
self.printDebug("mb3sync Service -> onPlayBackStarted" + currentFile)
# grab all the info about this item from the stored windows props
# only ever use the win props here, use the data map in all other places
deleteurl = WINDOW.getProperty(currentFile + "deleteurl")
runtime = WINDOW.getProperty(currentFile + "runtimeticks")
item_id = WINDOW.getProperty(currentFile + "item_id")
refresh_id = WINDOW.getProperty(currentFile + "refresh_id")
audioindex = WINDOW.getProperty(currentFile + "AudioStreamIndex")
subtitleindex = WINDOW.getProperty(currentFile + "SubtitleStreamIndex")
playMethod = WINDOW.getProperty(currentFile + "playmethod")
itemType = WINDOW.getProperty(currentFile + "type")
seekTime = WINDOW.getProperty(currentFile + "seektime")
if seekTime != "":
self.seekToPosition(int(seekTime))
if(item_id == None or len(item_id) == 0):
return
url = ("http://%s:%s/mediabrowser/Sessions/Playing" % (addonSettings.getSetting('ipaddress'), addonSettings.getSetting('port')))
url = url + "?itemId=" + item_id
url = url + "&canSeek=true"
url = url + "&PlayMethod=" + playMethod
url = url + "&QueueableMediaTypes=Video"
url = url + "&MediaSourceId=" + item_id
if(audioindex != None and audioindex!=""):
url = url + "&AudioStreamIndex=" + audioindex
if(subtitleindex != None and subtitleindex!=""):
url = url + "&SubtitleStreamIndex=" + subtitleindex
self.downloadUtils.downloadUrl(url, postBody="", type="POST")
# save data map for updates and position calls
data = {}
data["deleteurl"] = deleteurl
data["runtime"] = runtime
data["item_id"] = item_id
data["refresh_id"] = refresh_id
data["currentfile"] = currentFile
data["AudioStreamIndex"] = audioindex
data["SubtitleStreamIndex"] = subtitleindex
data["playmethod"] = playMethod
data["Type"] = itemType
self.played_information[currentFile] = data
self.printDebug("mb3sync Service -> ADDING_FILE : " + currentFile)
self.printDebug("mb3sync Service -> ADDING_FILE : " + str(self.played_information))
# log some playback stats
if(itemType != None):
if(self.playStats.get(itemType) != None):
count = self.playStats.get(itemType) + 1
self.playStats[itemType] = count
else:
self.playStats[itemType] = 1
if(playMethod != None):
if(self.playStats.get(playMethod) != None):
count = self.playStats.get(playMethod) + 1
self.playStats[playMethod] = count
else:
self.playStats[playMethod] = 1
# reset in progress position
self.reportPlayback()
def GetPlayStats(self):
return self.playStats
def onPlayBackEnded( self ):
# Will be called when xbmc stops playing a file
self.printDebug("mb3sync Service -> onPlayBackEnded")
self.stopAll()
def onPlayBackStopped( self ):
# Will be called when user stops xbmc playing a file
self.printDebug("mb3sync Service -> onPlayBackStopped")
self.stopAll()
def seekToPosition(self, seekTo):
#Jump to resume point
jumpBackSec = 10
seekToTime = seekTo - jumpBackSec
count = 0
while xbmc.Player().getTime() < (seekToTime - 5) and count < 11: # only try 10 times
count = count + 1
xbmc.Player().pause
xbmc.sleep(100)
xbmc.Player().seekTime(seekToTime)
xbmc.sleep(100)
xbmc.Player().play()

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

@ -0,0 +1,162 @@
#################################################################################################
# utils
#################################################################################################
import xbmc
import xbmcgui
import xbmcaddon
import xbmcvfs
import json
import os
import inspect
from xml.etree.ElementTree import Element, SubElement, Comment, tostring
from xml.etree import ElementTree
from xml.dom import minidom
import xml.etree.cElementTree as ET
from API import API
from PlayUtils import PlayUtils
from DownloadUtils import DownloadUtils
downloadUtils = DownloadUtils()
addonSettings = xbmcaddon.Addon(id='plugin.video.mb3sync')
language = addonSettings.getLocalizedString
def logMsg(title, msg, level = 1):
#todo --> get this from a setting
logLevel = 0
if(logLevel >= level):
if(logLevel == 1):
try:
xbmc.log(title + " -> " + inspect.stack()[1][3] + " : " + str(msg))
except UnicodeEncodeError:
xbmc.log(title + " -> " + inspect.stack()[1][3] + " : " + str(msg.encode('utf-8')))
else:
try:
xbmc.log(title + " -> " + str(msg))
except UnicodeEncodeError:
xbmc.log(title + " -> " + str(msg.encode('utf-8')))
def checkKodiSources():
print "All sources in Kodi -->"
addon = xbmcaddon.Addon(id='plugin.video.mb3sync')
addondir = xbmc.translatePath( addon.getAddonInfo('profile') )
dataPath = os.path.join(addondir,"library")
movieLibrary = os.path.join(dataPath,'movies')
tvLibrary = os.path.join(dataPath,'tvshows')
if not xbmcvfs.exists(dataPath):
xbmcvfs.mkdir(dataPath)
if not xbmcvfs.exists(movieLibrary):
xbmcvfs.mkdir(movieLibrary)
if not xbmcvfs.exists(tvLibrary):
xbmcvfs.mkdir(tvLibrary)
allKodiSources = list()
json_response = xbmc.executeJSONRPC('{"jsonrpc": "2.0", "method": "Files.GetSources", "params": { "media": "video"}, "id": 1 }')
jsonobject = json.loads(json_response.decode('utf-8','replace'))
if(jsonobject.has_key('result')):
result = jsonobject['result']
if(result.has_key('sources')):
for source in result["sources"]:
allKodiSources.append(source["label"])
allKodiSources = set(allKodiSources)
rebootRequired = False
if not "mediabrowser_movies" in allKodiSources:
addKodiSource("mediabrowser_movies",movieLibrary)
rebootRequired = True
if not "mediabrowser_tvshows" in allKodiSources:
addKodiSource("mediabrowser_tvshows",tvLibrary)
rebootRequired = True
if rebootRequired:
ret = xbmcgui.Dialog().yesno(heading="MediaBrowser Sync service", line1="A restart of Kodi is needed to apply changes. Do you want to reboot now ?")
if ret:
xbmc.executebuiltin("RestartApp")
def addKodiSource(name, path):
userDataPath = xbmc.translatePath( "special://profile" )
sourcesFile = os.path.join(userDataPath,'sources.xml')
print "####parsing sources file #####" + sourcesFile
tree = ET.ElementTree(file=sourcesFile)
root = tree.getroot()
videosources = root.find("video")
#remove any existing entries
allsources = videosources.findall("source")
if allsources != None:
for source in allsources:
if source.find("name").text == name:
videosources.remove(source)
# add new source
source = SubElement(videosources,'source')
SubElement(source, "name").text = name
SubElement(source, "path").text = path
tree.write(sourcesFile)
def checkAuthentication():
#check authentication
if addonSettings.getSetting('username') != "" and addonSettings.getSetting('ipaddress') != "":
try:
downloadUtils.authenticate()
except Exception, e:
logMsg("MB3 Syncer authentication failed",e)
pass
def prettifyXml(elem):
rough_string = etree.tostring(elem, "utf-8")
reparsed = minidom.parseString(rough_string)
return reparsed.toprettyxml(indent="\t")
def doKodiCleanup():
#remove old testdata and remove missing files
json_response = xbmc.executeJSONRPC('{"jsonrpc": "2.0", "method": "VideoLibrary.GetMovies", "params": {"properties" : ["file"], "sort": { "order": "ascending", "method": "label", "ignorearticle": true } }, "id": "libMovies"}')
jsonobject = json.loads(json_response.decode('utf-8','replace'))
if(jsonobject.has_key('result')):
result = jsonobject['result']
if(result.has_key('movies')):
movies = result['movies']
for movie in movies:
if (xbmcvfs.exists(movie["file"]) == False) or ("plugin.video.xbmb3c" in movie["file"]):
print "deleting --> " + movie["file"]
xbmc.executeJSONRPC('{"jsonrpc": "2.0", "method": "VideoLibrary.RemoveMovie", "params": { "movieid": %i}, "id": 1 }' %(movie["movieid"]))
def get_params( paramstring ):
xbmc.log("Parameter string: " + paramstring)
param={}
if len(paramstring)>=2:
params=paramstring
if params[0] == "?":
cleanedparams=params[1:]
else:
cleanedparams=params
if (params[len(params)-1]=='/'):
params=params[0:len(params)-2]
pairsofparams=cleanedparams.split('&')
for i in range(len(pairsofparams)):
splitparams={}
splitparams=pairsofparams[i].split('=')
if (len(splitparams))==2:
param[splitparams[0]]=splitparams[1]
elif (len(splitparams))==3:
param[splitparams[0]]=splitparams[1]+"="+splitparams[2]
xbmc.log("XBMB3C -> Detected parameters: " + str(param))
return param

View File

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

BIN
resources/mb3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 117 KiB

17
resources/settings.xml Normal file
View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<settings>
<category label="30014"> <!-- MediaBrowser -->
<setting id="ipaddress" type="text" label="30000" default="" 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="30022"> <!-- Advanced -->
<setting id="logLevel" type="enum" label="30004" values="None|Info|Debug" default="0" />
<setting id="profile" type="bool" label="30010" default="false" visible="true" enable="true" />
<setting id="reportMetrics" type="bool" label="30224" default="true" visible="true" enable="true" />
<setting id="skinMessageIgnored" type="bool" label="30233" default="false" visible="true" enable="true" />
</category>
</settings>

101
service.py Normal file
View File

@ -0,0 +1,101 @@
import xbmcaddon
import xbmc
import xbmcgui
import os
import threading
import json
from datetime import datetime
addonSettings = xbmcaddon.Addon(id='plugin.video.mb3sync')
cwd = addonSettings.getAddonInfo('path')
BASE_RESOURCE_PATH = xbmc.translatePath( os.path.join( cwd, 'resources', 'lib' ) )
sys.path.append(BASE_RESOURCE_PATH)
WINDOW = xbmcgui.Window( 10000 )
import KodiMonitor
import Utils as utils
from LibrarySync import LibrarySync
from Player import Player
librarySync = LibrarySync()
class Service():
def __init__(self, *args ):
self.KodiMonitor = KodiMonitor.Kodi_Monitor()
utils.logMsg("MB3 Sync Service" "starting Monitor",0)
pass
def ServiceEntryPoint(self):
player = Player()
lastProgressUpdate = datetime.today()
#perform kodi cleanup (needed while testing, can be removed later if needed)
utils.doKodiCleanup()
# check kodi library sources
utils.checkKodiSources()
interval_FullSync = 120
interval_IncrementalSync = 30
cur_seconds_fullsync = 0
cur_seconds_incrsync = 0
while not xbmc.abortRequested:
xbmc.sleep(1000)
if xbmc.Player().isPlaying():
try:
playTime = xbmc.Player().getTime()
currentFile = xbmc.Player().getPlayingFile()
if(player.played_information.get(currentFile) != None):
player.played_information[currentFile]["currentPossition"] = playTime
# send update
td = datetime.today() - lastProgressUpdate
secDiff = td.seconds
if(secDiff > 10):
try:
player.reportPlayback()
except Exception, msg:
xbmc.log("MB3 Sync Service -> Exception reporting progress : " + msg)
pass
lastProgressUpdate = datetime.today()
except Exception, e:
xbmc.log("MB3 Sync Service -> Exception in Playback Monitor Service : " + str(e))
pass
else:
# background worker for database sync
if WINDOW.getProperty("mb3_authenticated") == "true":
#full sync
if((interval_FullSync >= cur_seconds_fullsync)):
librarySync.syncDatabase()
cur_seconds_fullsync = interval_FullSync
else:
cur_seconds_fullsync -= 1
#incremental sync
if((interval_IncrementalSync >= cur_seconds_incrsync)):
librarySync.updatePlayCounts()
cur_seconds_incrsync = interval_IncrementalSync
else:
cur_seconds_incrsync -= 1
else:
utils.checkAuthentication()
utils.logMsg("MB3 Sync Service" "stopping Service",0)
#start the service
Service().ServiceEntryPoint()