Merge branch 'master' into translations

This commit is contained in:
Croneter 2018-04-10 19:31:10 +02:00
commit bc36231454
15 changed files with 372 additions and 429 deletions

View file

@ -1,5 +1,5 @@
[![stable version](https://img.shields.io/badge/stable_version-1.8.18-blue.svg?maxAge=60&style=flat) ](https://github.com/croneter/binary_repo/raw/master/stable/repository.plexkodiconnect/repository.plexkodiconnect-1.0.2.zip)
[![beta version](https://img.shields.io/badge/beta_version-2.0.16-red.svg?maxAge=60&style=flat) ](https://github.com/croneter/binary_repo/raw/master/beta/repository.plexkodiconnectbeta/repository.plexkodiconnectbeta-1.0.2.zip)
[![beta version](https://img.shields.io/badge/beta_version-2.0.18-red.svg?maxAge=60&style=flat) ](https://github.com/croneter/binary_repo/raw/master/beta/repository.plexkodiconnectbeta/repository.plexkodiconnectbeta-1.0.2.zip)
[![Installation](https://img.shields.io/badge/wiki-installation-brightgreen.svg?maxAge=60&style=flat)](https://github.com/croneter/PlexKodiConnect/wiki/Installation)
[![FAQ](https://img.shields.io/badge/wiki-FAQ-brightgreen.svg?maxAge=60&style=flat)](https://github.com/croneter/PlexKodiConnect/wiki/faq)
@ -15,33 +15,28 @@ PKC combines the best of Kodi - ultra smooth navigation, beautiful and highly cu
Have a look at [some screenshots](https://github.com/croneter/PlexKodiConnect/wiki/Some-PKC-Screenshots) to see what's possible.
### UPDATE YOUR PKC REPO TO RECEIVE UPDATES!
### Update Your PKC Repo to Receive Updates!
Unfortunately, the PKC Kodi repository had to move because it stopped working (thanks https://bintray.com). If you installed PKC before December 15, 2017, you need to [**MANUALLY** update the repo once](https://github.com/croneter/PlexKodiConnect/wiki/Update-PKC-Repository).
### Please Help Translating
Please help translate PlexKodiConnect into your language: [Transifex.com](https://www.transifex.com/croneter/pkc)
### Content
* [**Warning**](#warning)
* [**What does PKC do?**](#what-does-pkc-do)
* [**PKC Features**](#pkc-features)
* [**Download and Installation**](#download-and-installation)
* [**What does PKC do?**](#what-does-pkc-do)
* [**Warning**](#warning)
* [**PKC Features**](#pkc-features)
* [**Additional Artwork**](#additional-artwork)
* [**Important notes**](#important-notes)
* [**Donations**](#donations)
* [**Request a New Feature**](#request-a-new-feature)
* [**Known Larger Issues**](#known-larger-issues)
* [**Issues being worked on**](#issues-being-worked-on)
* [**Issues and Bugs**](#issues-and-bugs)
* [**Credits**](#credits)
### Warning
Use at your own risk! This plugin assumes that you manage all your videos with Plex (and none with Kodi). You might lose data already stored in the Kodi video and music databases as this plugin directly changes them. Don't worry if you want Plex to manage all your media (like you should ;-)).
### Download and Installation
Some people argue that PKC is 'hacky' because of the way it directly accesses the Kodi database. See [here for a more thorough discussion](https://github.com/croneter/PlexKodiConnect/wiki/Is-PKC-'hacky'%3F).
Install PKC via the PlexKodiConnect Kodi repository download button just below (do NOT use the standard GitHub download!). See the [github wiki installation manual](https://github.com/croneter/PlexKodiConnect/wiki/Installation) for a detailed guide. Please use the stable version except if you really know what you're doing. Kodi will update PKC automatically.
| Stable version | Beta version |
|----------------|--------------|
| [![stable version](https://img.shields.io/badge/stable_version-latest-blue.svg?maxAge=60&style=flat) ](https://github.com/croneter/binary_repo/raw/master/stable/repository.plexkodiconnect/repository.plexkodiconnect-1.0.2.zip) | [![beta version](https://img.shields.io/badge/beta_version-latest-red.svg?maxAge=60&style=flat) ](https://github.com/croneter/binary_repo/raw/master/beta/repository.plexkodiconnectbeta/repository.plexkodiconnectbeta-1.0.2.zip) |
### What does PKC do?
PKC synchronizes your media from your Plex server to the native Kodi database. Hence:
@ -51,8 +46,14 @@ PKC synchronizes your media from your Plex server to the native Kodi database. H
- Automatically get additional artwork (more than Plex offers)
- Enjoy Plex features using the Kodi interface
### Warning
Use at your own risk! This plugin assumes that you manage all your videos with Plex (and none with Kodi). You might lose data already stored in the Kodi video and music databases as this plugin directly changes them. Don't worry if you want Plex to manage all your media (like you should ;-)).
Some people argue that PKC is 'hacky' because of the way it directly accesses the Kodi database. See [here for a more thorough discussion](https://github.com/croneter/PlexKodiConnect/wiki/Is-PKC-'hacky'%3F).
### PKC Features
- Support of Kodi 18 Leia (and Kodi 17 Krypton)
- [Amazon Alexa voice recognition](https://www.plex.tv/apps/streaming-devices/amazon-alexa)
- [Plex Watch Later / Plex It!](https://support.plex.tv/hc/en-us/sections/200211783-Plex-It-)
- [Plex Companion](https://support.plex.tv/hc/en-us/sections/200276908-Plex-Companion): fling Plex media (or anything else) from other Plex devices to PlexKodiConnect
@ -75,28 +76,13 @@ PKC synchronizes your media from your Plex server to the native Kodi database. H
+ Norwegian, thanks @mjorud
+ Portuguese, thanks @goncalo532
+ Russian, thanks @UncleStark
+ [Please help translating](https://www.transifex.com/croneter/pkc)
### Download and Installation
Install PKC via the PlexKodiConnect Kodi repository download button just below (do NOT use the standard GitHub download!). See the [github wiki installation manual](https://github.com/croneter/PlexKodiConnect/wiki/Installation) for a detailed guide. Please use the stable version except if you really know what you're doing. Kodi will update PKC automatically.
| Stable version | Beta version |
|----------------|--------------|
| [![stable version](https://img.shields.io/badge/stable_version-latest-blue.svg?maxAge=60&style=flat) ](https://github.com/croneter/binary_repo/raw/master/stable/repository.plexkodiconnect/repository.plexkodiconnect-1.0.2.zip) | [![beta version](https://img.shields.io/badge/beta_version-latest-red.svg?maxAge=60&style=flat) ](https://github.com/croneter/binary_repo/raw/master/beta/repository.plexkodiconnectbeta/repository.plexkodiconnectbeta-1.0.2.zip) |
+ Hungarian, thanks @savage93
+ [You can easily help to translate PKC!](https://www.transifex.com/croneter/pkc)
### Additional Artwork
PKC uses additional artwork for free from [TheMovieDB](https://www.themoviedb.org). Many thanks for lettings us use the API, guys!
[![Logo of TheMovieDB](themoviedb.png)](https://www.themoviedb.org)
### Important Notes
1. If you are using a **low CPU device like a Raspberry Pi or a CuBox**, PKC might be instable or crash during initial sync. Lower the number of threads in the [PKC settings under Sync Options](https://github.com/croneter/PlexKodiConnect/wiki/PKC-settings#sync-options). Don't forget to reboot Kodi after that.
2. **Compatibility**:
* PKC is currently not compatible with Kodi's Video Extras plugin. **Deactivate Video Extras** if trailers/movies start randomly playing.
* PKC is not (and will never be) compatible with the **MySQL database replacement** in Kodi. In fact, PKC replaces the MySQL functionality because it acts as a "man in the middle" for your entire media library.
* If **another plugin is not working** like it's supposed to, try to use [PKC direct paths](https://github.com/croneter/PlexKodiConnect/wiki/Direct-Paths-Explained)
### Donations
I'm not in any way affiliated with Plex. Thank you very much for a small donation via ko-fi.com and PayPal, Bitcoin or Ether if you appreciate PKC.
**Full disclaimer:** I will see your name and address if you use PayPal. Rest assured that I will not share this with anyone.
@ -116,18 +102,7 @@ I'm not in any way affiliated with Plex. Thank you very much for a small donatio
[![Feature Requests](http://feathub.com/croneter/PlexKodiConnect?format=svg)](http://feathub.com/croneter/PlexKodiConnect)
### Known Larger Issues
Solutions are unlikely due to the nature of these issues
- A Plex Media Server "bug" leads to frequent and slow syncs, see [here for more info](https://github.com/croneter/PlexKodiConnect/issues/135)
- *Plex Music when using Addon paths instead of Native Direct Paths:* Kodi tries to scan every(!) single Plex song on startup. This leads to errors in the Kodi log file and potentially even crashes. See the [Github issues](https://github.com/croneter/PlexKodiConnect/issues/14) for more details. **Workaround**: use [PKC direct paths](https://github.com/croneter/PlexKodiConnect/wiki/Set-up-Direct-Paths) instead of addon paths.
*Background Sync:*
The Plex Server does not tell anyone of the following changes. Hence PKC cannot detect these changes instantly but will notice them only on full/delta syncs (standard settings is every 60 minutes)
- Toggle the viewstate of an item to (un)watched outside of Kodi
### Issues being worked on
### Issues and Bugs
Have a look at the [Github Issues Page](https://github.com/croneter/PlexKodiConnect/issues). Before you open your own issue, please read [How to report a bug](https://github.com/croneter/PlexKodiConnect/wiki/How-to-Report-A-Bug).

View file

@ -1,10 +1,10 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<addon id="plugin.video.plexkodiconnect" name="PlexKodiConnect" version="2.0.16" provider-name="croneter">
<addon id="plugin.video.plexkodiconnect" name="PlexKodiConnect" version="2.0.18" provider-name="croneter">
<requires>
<import addon="xbmc.python" version="2.1.0"/>
<import addon="script.module.requests" version="2.9.1" />
<import addon="plugin.video.plexkodiconnect.movies" version="2.0.0" />
<import addon="plugin.video.plexkodiconnect.tvshows" version="2.0.1" />
<import addon="plugin.video.plexkodiconnect.movies" version="2.0.1" />
<import addon="plugin.video.plexkodiconnect.tvshows" version="2.0.2" />
</requires>
<extension point="xbmc.python.pluginsource" library="default.py">
<provides>video audio image</provides>
@ -25,9 +25,6 @@
<website>https://github.com/croneter/PlexKodiConnect</website>
<email></email>
<source>https://github.com/croneter/PlexKodiConnect</source>
<summary lang="en_GB">Native Integration of Plex into Kodi</summary>
<description lang="en_GB">Connect Kodi to your Plex Media Server. This plugin assumes that you manage all your videos with Plex (and none with Kodi). You might lose data already stored in the Kodi video and music databases (as this plugin directly changes them). Use at your own risk!</description>
<disclaimer lang="en_GB">Use at your own risk</disclaimer>
<summary lang="nl_NL">Directe integratie van Plex in Kodi</summary>
<description lang="nl_NL">Verbind Kodi met je Plex Media Server. Deze plugin gaat ervan uit dat je al je video's met Plex (en niet met Kodi) beheerd. Je kunt gegevens reeds opgeslagen in de databases voor video en muziek van Kodi (deze plugin wijzigt deze gegevens direct) verliezen. Gebruik op eigen risico!</description>
<disclaimer lang="nl_NL">Gebruik op eigen risico</disclaimer>
@ -38,8 +35,8 @@
<description lang="fr_FR">Connecter Kodi à votre Plex Media Server. Ce plugin assume que vous souhaitez gérer toutes vos vidéos avec Plex (et aucune avec Kodi). Vous pourriez perdre les données déjà stockées dans les bases de données vidéo et musique de Kodi (ce plugin les modifie directement). Utilisez à vos propres risques !</description>
<disclaimer lang="fr_FR">A utiliser à vos propres risques</disclaimer>
<summary lang="de_DE">Komplette Integration von Plex in Kodi</summary>
<description lang="de_DE">Verbindet Kodi mit deinem Plex Media Server. Dieses Addon geht davon aus, dass du all deine Videos mit Plex verwaltest (und keine direkt mit Kodi). Du wirst möglicherweise Daten verlieren, die bereits in der Kodi Video- und/oder Musik-Datenbank gespeichert sind (da dieses Addon beide Datenbanken direkt verändert). Verwendung auf eigene Gefahr!</description>
<disclaimer lang="de_DE">Verwendung auf eigene Gefahr</disclaimer>
<description lang="de_DE">Verbindet Kodi mit deinem Plex Media Server. Dieses Addon geht davon aus, dass du all deine Videos mit Plex verwaltest (und keine direkt mit Kodi). Du wirst möglicherweise Daten verlieren, die bereits in der Kodi Video- und/oder Musik-Datenbank gespeichert sind (da dieses Addon beide Datenbanken direkt verändert). Benutzung auf eigene Gefahr!</description>
<disclaimer lang="de_DE">Benutzung auf eigene Gefahr</disclaimer>
<summary lang="pt_PT">Integração nativa do Plex no Kodi</summary>
<description lang="pt_PT">Conectar o Kodi ao Servidor Plex Media. Este plugin assume que gerirá todos os vídeos com o Plex (e nenhum com Kodi). Poderá perder dados guardados nas bases de dados de vídeo e musica do Kodi (pois este plugin interfere diretamente com as mesmas). Use por risco de conta própria</description>
<disclaimer lang="pt_PT">Use por risco de conta própria</disclaimer>
@ -61,7 +58,33 @@
<summary lang="da_DK">Indbygget Integration af Plex i Kodi</summary>
<description lang="da_DK">Tilslut Kodi til din Plex Media Server. Dette plugin forudsætter, at du administrere alle dine videoer med Plex (og ikke med Kodi). Du kan miste data som allerede er gemt i Kodi video og musik-databaser (dette plugin ændrer direkte i dem). Brug på eget ansvar!</description>
<disclaimer lang="da_DK">Brug på eget ansvar</disclaimer>
<news>version 2.0.16 (beta only):
<summary lang="no_NO">Naturlig integrasjon av Plex til Kodi</summary>
<description lang="no_NO">Koble Kodi til din Plex Media Server. Denne plugin forventer at du organiserer alle dine videor med Plex (og ingen med Kodi). Du kan miste all data allerede lagret i Kodi video- og musikkdatabasene (da denne plugin umiddelbart forandrer dem). Bruk på egen risiko!</description>
<disclaimer lang="no_NO">Bruk på eget ansvar</disclaimer>
<summary lang="hu_HU">a Plex natív integrációja a Kodi-ba</summary>
<description lang="hu_HU">Csatlakoztassa a Kodi-t a Plex médiaszerveréhez. Ez a kiegészítő feltételezi, hogy az összes videóját a Plex-szel kezeli (és egyiket sem a Kodi-val). Elveszítheti a már a Kodi videó- és zene-adatbázisában tárolt adatokat (mivel ez a kiegészítő közvetlenül módosítja az adatbázisokat). Csak saját felelősségére használja!</description>
<disclaimer lang="hu_HU">Csak saját felelősségre használja</disclaimer>
<summary lang="ru_RU">Нативная интеграция сервера Plex в Kodi</summary>
<description lang="ru_RU">Подключите Kodi к своему серверу Plex. Плагин предполагает что вы управляете своими видео с помощью Plex (а не в Kodi). Вы можете потерять текущие базы данных музыки и видео в Kodi (так как плагин напрямую их изменяет). Используйте на свой страх и риск</description>
<disclaimer lang="ru_RU">Используйте на свой страх и риск</disclaimer>
<news>version 2.0.18 (beta only):
- Fix some playqueue inconsistencies using Plex Companion
- Direct paths: fix replaying item where playback was started via PMS
- Fix Plex trailers screwing up playqueue
- Fix IndexError when emptying Kodi playqueue
- Incorporate PKC player in kodimonitor module
- Fix pretty printing of PKC playqueues not working
- Code cleanups
version 2.0.17 (beta only):
- Finally make PKC compatible with Kodi 18 Leia Alpha 1
- Fix information screen and Plex option not working
- Activate Kodi background updates to hide "Compressing Database"
- Update translations
- Remove most strings not being used by PKC
- Remove some legacy settings
version 2.0.16 (beta only):
- Do NOT delete playstates before getting new ones from the PMS
version 2.0.15 (beta only):

View file

@ -1,3 +1,20 @@
version 2.0.18 (beta only):
- Fix some playqueue inconsistencies using Plex Companion
- Direct paths: fix replaying item where playback was started via PMS
- Fix Plex trailers screwing up playqueue
- Fix IndexError when emptying Kodi playqueue
- Incorporate PKC player in kodimonitor module
- Fix pretty printing of PKC playqueues not working
- Code cleanups
version 2.0.17 (beta only):
- Finally make PKC compatible with Kodi 18 Leia Alpha 1
- Fix information screen and Plex option not working
- Activate Kodi background updates to hide "Compressing Database"
- Update translations
- Remove most strings not being used by PKC
- Remove some legacy settings
version 2.0.16 (beta only):
- Do NOT delete playstates before getting new ones from the PMS

View file

@ -7,7 +7,7 @@ from Queue import Empty
from socket import SHUT_RDWR
from urllib import urlencode
from xbmc import sleep, executebuiltin
from xbmc import sleep, executebuiltin, Player
from utils import settings, thread_methods, language as lang, dialog
from plexbmchelper import listener, plexgdm, subscribers, httppersist
@ -18,7 +18,6 @@ from playlist_func import get_pms_playqueue, get_plextype_from_xml, \
get_playlist_details_from_xml
from playback import playback_triage, play_xml
import json_rpc as js
import player
import variables as v
import state
import playqueue as PQ
@ -43,7 +42,7 @@ class PlexCompanion(Thread):
self.client.clientDetails()
LOG.debug("Registration string is:\n%s", self.client.getClientDetails())
# kodi player instance
self.player = player.PKC_Player()
self.player = Player()
self.httpd = False
self.subscription_manager = None
Thread.__init__(self)

View file

@ -636,7 +636,7 @@ def init_plex_playqueue(itemid, librarySectionUUID, mediatype='movie',
xml[0].tag
except (IndexError, TypeError, AttributeError):
LOG.error("Error retrieving metadata for %s", url)
return None
return
return xml

View file

@ -6,8 +6,8 @@ import xml.etree.ElementTree as etree
from xbmc import executebuiltin, translatePath
from utils import settings, window, language as lang, try_encode, try_decode, \
XmlKodiSetting, reboot_kodi, dialog
from utils import settings, window, language as lang, try_decode, dialog, \
XmlKodiSetting, reboot_kodi
from migration import check_migration
from downloadutils import DownloadUtils as DU
from userclient import UserClient
@ -28,13 +28,13 @@ LOG = getLogger("PLEX." + __name__)
WINDOW_PROPERTIES = (
"plex_online", "plex_serverStatus", "plex_onWake", "plex_kodiScan",
"plex_shouldStop", "plex_dbScan", "plex_initialScan",
"plex_customplayqueue", "plex_playbackProps", "pms_token", "plex_token",
"pms_server", "plex_machineIdentifier", "plex_servername",
"plex_authenticated", "PlexUserImage", "useDirectPaths", "countError",
"countUnauthorized", "plex_restricteduser", "plex_allows_mediaDeletion",
"plex_command", "plex_result", "plex_force_transcode_pix"
"plex_online", "plex_serverStatus", "plex_shouldStop", "plex_dbScan",
"plex_initialScan", "plex_customplayqueue", "plex_playbackProps",
"pms_token", "plex_token", "pms_server", "plex_machineIdentifier",
"plex_servername", "plex_authenticated", "PlexUserImage", "useDirectPaths",
"countError", "countUnauthorized", "plex_restricteduser",
"plex_allows_mediaDeletion", "plex_command", "plex_result",
"plex_force_transcode_pix"
)

View file

@ -999,7 +999,7 @@ class TVShows(Items):
"""
plex_dbitem = self.plex_db.getItem_byId(plex_id)
if plex_dbitem is None:
LOG.info('Cannot delete plex_id %s - not found in DB', plex_id)
LOG.debug('Cannot delete plex_id %s - not found in DB', plex_id)
return
kodi_id = plex_dbitem[0]
file_id = plex_dbitem[1]
@ -1075,7 +1075,7 @@ class TVShows(Items):
if v.KODIVERSION >= 17:
self.kodi_db.remove_uniqueid(kodi_id, v.KODI_TYPE_SHOW)
self.kodi_db.remove_ratings(kodi_id, v.KODI_TYPE_SHOW)
LOG.info("Removed tvshow: %s", kodi_id)
LOG.debug("Removed tvshow: %s", kodi_id)
def remove_season(self, kodi_id):
"""
@ -1086,7 +1086,7 @@ class TVShows(Items):
self.kodicursor)
self.kodicursor.execute("DELETE FROM seasons WHERE idSeason = ?",
(kodi_id,))
LOG.info("Removed season: %s", kodi_id)
LOG.debug("Removed season: %s", kodi_id)
def remove_episode(self, kodi_id, file_id):
"""
@ -1102,7 +1102,7 @@ class TVShows(Items):
if v.KODIVERSION >= 17:
self.kodi_db.remove_uniqueid(kodi_id, v.KODI_TYPE_EPISODE)
self.kodi_db.remove_ratings(kodi_id, v.KODI_TYPE_EPISODE)
LOG.info("Removed episode: %s", kodi_id)
LOG.debug("Removed episode: %s", kodi_id)
class Music(Items):
@ -1719,109 +1719,101 @@ class Music(Items):
# Update album artwork
artwork.modify_artwork(artworks, albumid, v.KODI_TYPE_ALBUM, kodicursor)
def remove(self, itemid):
def remove(self, plex_id):
"""
Remove kodiid, file_id, pathid, plex reference
Completely remove the item with plex_id from the Kodi and Plex DBs.
Orphaned entries will also be deleted.
"""
plex_db = self.plex_db
plex_dbitem = plex_db.getItem_byId(itemid)
plex_dbitem = self.plex_db.getItem_byId(plex_id)
try:
kodiid = plex_dbitem[0]
mediatype = plex_dbitem[4]
LOG.info("Removing %s kodiid: %s", mediatype, kodiid)
kodi_id = plex_dbitem[0]
file_id = plex_dbitem[1]
parent_id = plex_dbitem[3]
kodi_type = plex_dbitem[4]
LOG.info("Removing %s with kodi_id: %s, parent_id: %s, file_id: %s",
kodi_type, kodi_id, parent_id, file_id)
except TypeError:
LOG.debug('Cannot delete item with plex id %s from Kodi', plex_id)
return
##### PROCESS ITEM #####
# Remove the plex reference
plex_db.removeItem(itemid)
##### IF SONG #####
if mediatype == v.KODI_TYPE_SONG:
self.plex_db.removeItem(plex_id)
##### SONG #####
if kodi_type == v.KODI_TYPE_SONG:
# Delete song
self.removeSong(kodiid)
# This should only address single song scenario, where server doesn't actually
# create an album for the song.
plex_db.removeWildItem(itemid)
self.remove_song(kodi_id)
# Album verification
for item in plex_db.getItem_byWildId(itemid):
for item in self.plex_db.getItem_byWildId(plex_id):
item_kid = item[0]
item_mediatype = item[1]
item_kodi_type = item[1]
if item_mediatype == v.KODI_TYPE_ALBUM:
childs = plex_db.getItem_byParentId(item_kid,
v.KODI_TYPE_SONG)
if item_kodi_type == v.KODI_TYPE_ALBUM:
childs = self.plex_db.getItem_byParentId(item_kid,
v.KODI_TYPE_SONG)
if not childs:
# Delete album
self.removeAlbum(item_kid)
self.remove_album(item_kid)
##### IF ALBUM #####
elif mediatype == v.KODI_TYPE_ALBUM:
##### ALBUM #####
elif kodi_type == v.KODI_TYPE_ALBUM:
# Delete songs, album
album_songs = plex_db.getItem_byParentId(kodiid,
v.KODI_TYPE_SONG)
album_songs = self.plex_db.getItem_byParentId(kodi_id,
v.KODI_TYPE_SONG)
for song in album_songs:
self.removeSong(song[1])
self.remove_song(song[1])
# Remove plex songs
plex_db.removeItems_byParentId(kodiid,
v.KODI_TYPE_SONG)
self.plex_db.removeItems_byParentId(kodi_id,
v.KODI_TYPE_SONG)
# Remove the album
self.removeAlbum(kodiid)
self.remove_album(kodi_id)
##### IF ARTIST #####
elif mediatype == v.KODI_TYPE_ARTIST:
elif kodi_type == v.KODI_TYPE_ARTIST:
# Delete songs, album, artist
albums = plex_db.getItem_byParentId(kodiid,
v.KODI_TYPE_ALBUM)
albums = self.plex_db.getItem_byParentId(kodi_id, v.KODI_TYPE_ALBUM)
for album in albums:
albumid = album[1]
album_songs = plex_db.getItem_byParentId(albumid,
v.KODI_TYPE_SONG)
album_songs = self.plex_db.getItem_byParentId(albumid,
v.KODI_TYPE_SONG)
for song in album_songs:
self.removeSong(song[1])
self.remove_song(song[1])
# Remove plex song
plex_db.removeItems_byParentId(albumid,
v.KODI_TYPE_SONG)
self.plex_db.removeItems_byParentId(albumid, v.KODI_TYPE_SONG)
# Remove plex artist
plex_db.removeItems_byParentId(albumid,
v.KODI_TYPE_ARTIST)
self.plex_db.removeItems_byParentId(albumid, v.KODI_TYPE_ARTIST)
# Remove kodi album
self.removeAlbum(albumid)
self.remove_album(albumid)
# Remove plex albums
plex_db.removeItems_byParentId(kodiid,
v.KODI_TYPE_ALBUM)
self.plex_db.removeItems_byParentId(kodi_id, v.KODI_TYPE_ALBUM)
# Remove artist
self.removeArtist(kodiid)
self.remove_artist(kodi_id)
LOG.info("Deleted %s: %s from kodi database", mediatype, itemid)
LOG.debug("Deleted plex_id %s from kodi database", plex_id)
def removeSong(self, kodiid):
def remove_song(self, kodi_id):
"""
Remove song, and only the song
"""
self.artwork.delete_artwork(kodiid, v.KODI_TYPE_SONG, self.kodicursor)
self.artwork.delete_artwork(kodi_id, v.KODI_TYPE_SONG, self.kodicursor)
self.kodicursor.execute("DELETE FROM song WHERE idSong = ?",
(kodiid,))
(kodi_id,))
def removeAlbum(self, kodiid):
def remove_album(self, kodi_id):
"""
Remove an album, and only the album
"""
self.artwork.delete_artwork(kodiid, v.KODI_TYPE_ALBUM, self.kodicursor)
self.artwork.delete_artwork(kodi_id, v.KODI_TYPE_ALBUM, self.kodicursor)
self.kodicursor.execute("DELETE FROM album WHERE idAlbum = ?",
(kodiid,))
(kodi_id,))
def removeArtist(self, kodiid):
def remove_artist(self, kodi_id):
"""
Remove an artist, and only the artist
"""
self.artwork.delete_artwork(kodiid,
v.KODI_TYPE_ARTIST,
self.kodicursor)
self.artwork.delete_artwork(kodi_id,
v.KODI_TYPE_ARTIST,
self.kodicursor)
self.kodicursor.execute("DELETE FROM artist WHERE idArtist = ?",
(kodiid,))
(kodi_id,))

View file

@ -204,24 +204,20 @@ class KodiDBMethods(object):
self.cursor.execute(query, (file_id, path_id, filename, date_added))
return file_id
def clean_file_table(self):
def obsolete_file_ids(self):
"""
Hack: using Direct Paths, Kodi adds all addon paths to the files table
but without a dateAdded entry. This method cleans up all file entries
without a dateAdded entry - to be called after playback has ended.
Returns a list of (idFile,) tuples (ints) of all Kodi file ids that do
not have a dateAdded set (dateAdded is NULL) and the filename start with
'plugin://plugin.video.plexkodiconnect'
These entries should be deleted as they're created falsely by Kodi.
"""
self.cursor.execute('SELECT idFile FROM files WHERE dateAdded IS NULL')
files = self.cursor.fetchall()
self.cursor.execute('DELETE FROM files where dateAdded IS NULL')
for file in files:
self.cursor.execute('DELETE FROM bookmark WHERE idFile = ?',
(file[0],))
self.cursor.execute('DELETE FROM settings WHERE idFile = ?',
(file[0],))
self.cursor.execute('DELETE FROM streamdetails WHERE idFile = ?',
(file[0],))
self.cursor.execute('DELETE FROM stacktimes WHERE idFile = ?',
(file[0],))
query = '''
SELECT idFile FROM files
WHERE dateAdded IS NULL
AND strFilename LIKE \'plugin://plugin.video.plexkodiconnect%\'
'''
self.cursor.execute(query)
return self.cursor.fetchall()
def show_id_from_path(self, path):
"""
@ -241,10 +237,12 @@ class KodiDBMethods(object):
show_id = None
return show_id
def remove_file(self, file_id):
def remove_file(self, file_id, remove_orphans=True):
"""
Removes the entry for file_id from the files table. Will also delete
entries from the associated tables: bookmark, settings, streamdetails
entries from the associated tables: bookmark, settings, streamdetails.
If remove_orphans is true, this method will delete any orphaned path
entries in the Kodi path table
"""
self.cursor.execute('SELECT idPath FROM files WHERE idFile = ? LIMIT 1',
(file_id,))
@ -262,12 +260,13 @@ class KodiDBMethods(object):
(file_id,))
self.cursor.execute('DELETE FROM stacktimes WHERE idFile = ?',
(file_id,))
# Delete orphaned path entry
self.cursor.execute('SELECT idFile FROM files WHERE idPath = ? LIMIT 1',
(path_id,))
if self.cursor.fetchone() is None:
self.cursor.execute('DELETE FROM path WHERE idPath = ?',
(path_id,))
if remove_orphans:
# Delete orphaned path entry
query = 'SELECT idFile FROM files WHERE idPath = ? LIMIT 1'
self.cursor.execute(query, (path_id,))
if self.cursor.fetchone() is None:
self.cursor.execute('DELETE FROM path WHERE idPath = ?',
(path_id,))
def _modify_link_and_table(self, kodi_id, kodi_type, entries, link_table,
table, key):

View file

@ -6,13 +6,15 @@ from json import loads
from threading import Thread
import copy
from xbmc import Monitor, Player, sleep, getCondVisibility, getInfoLabel, \
getLocalizedString
import xbmc
from xbmcgui import Window
import plexdb_functions as plexdb
from utils import window, settings, plex_command, thread_methods, try_encode
import kodidb_functions as kodidb
from utils import window, settings, plex_command, thread_methods, try_encode, \
kodi_time_to_millis, unix_date_to_kodi, unix_timestamp
from PlexFunctions import scrobble
from downloadutils import DownloadUtils as DU
from kodidb_functions import kodiid_from_filename
from plexbmchelper.subscribers import LOCKER
from playback import playback_triage
@ -21,6 +23,7 @@ import playqueue as PQ
import json_rpc as js
import playlist_func as PL
import state
import variables as v
###############################################################################
@ -53,14 +56,14 @@ STATE_SETTINGS = {
###############################################################################
class KodiMonitor(Monitor):
class KodiMonitor(xbmc.Monitor):
"""
PKC implementation of the Kodi Monitor class. Invoke only once.
"""
def __init__(self):
self.xbmcplayer = Player()
self.xbmcplayer = xbmc.Player()
self._already_slept = False
Monitor.__init__(self)
xbmc.Monitor.__init__(self)
for playerid in state.PLAYER_STATES:
state.PLAYER_STATES[playerid] = copy.deepcopy(state.PLAYSTATE)
state.OLD_PLAYER_STATES[playerid] = copy.deepcopy(state.PLAYSTATE)
@ -71,16 +74,12 @@ class KodiMonitor(Monitor):
Will be called when Kodi starts scanning the library
"""
LOG.debug("Kodi library scan %s running.", library)
if library == "video":
window('plex_kodiScan', value="true")
def onScanFinished(self, library):
"""
Will be called when Kodi finished scanning the library
"""
LOG.debug("Kodi library scan %s finished.", library)
if library == "video":
window('plex_kodiScan', clear=True)
def onSettingsChanged(self):
"""
@ -141,7 +140,15 @@ class KodiMonitor(Monitor):
elif method == "Player.OnStop":
# Should refresh our video nodes, e.g. on deck
# xbmc.executebuiltin('ReloadSkin()')
pass
if data.get('end'):
if state.PKC_CAUSED_STOP is True:
state.PKC_CAUSED_STOP = False
state.PKC_CAUSED_STOP_DONE = True
LOG.debug('PKC caused this playback stop - ignoring')
else:
_playback_cleanup(ended=True)
else:
_playback_cleanup()
elif method == 'Playlist.OnAdd':
self._playlist_onadd(data)
elif method == 'Playlist.OnRemove':
@ -169,17 +176,11 @@ class KodiMonitor(Monitor):
LOG.error("Could not find itemid in plex database for a "
"video library update")
else:
# Stop from manually marking as watched unwatched, with
# actual playback.
if window('plex_skipWatched%s' % itemid) == "true":
# property is set in player.py
window('plex_skipWatched%s' % itemid, clear=True)
# notify the server
if playcount > 0:
scrobble(itemid, 'watched')
else:
# notify the server
if playcount > 0:
scrobble(itemid, 'watched')
else:
scrobble(itemid, 'unwatched')
scrobble(itemid, 'unwatched')
elif method == "VideoLibrary.OnRemove":
pass
elif method == "System.OnSleep":
@ -188,12 +189,11 @@ class KodiMonitor(Monitor):
window('plex_online', value="sleep")
elif method == "System.OnWake":
# Allow network to wake up
sleep(10000)
window('plex_onWake', value="true")
xbmc.sleep(10000)
window('plex_online', value="false")
elif method == "GUI.OnScreensaverDeactivated":
if settings('dbSyncScreensaver') == "true":
sleep(5000)
xbmc.sleep(5000)
plex_command('RUN_LIB_SCAN', 'full')
elif method == "System.OnQuit":
LOG.info('Kodi OnQuit detected - shutting down')
@ -254,6 +254,7 @@ class KodiMonitor(Monitor):
playqueue = PQ.PLAYQUEUES[data['playlistid']]
if not playqueue.is_pkc_clear():
playqueue.clear(kodi=False)
playqueue.pkc_edit = True
else:
LOG.debug('Detected PKC clear - ignoring')
@ -307,7 +308,7 @@ class KodiMonitor(Monitor):
# start as Kodi updates this info very late!! Might get previous
# element otherwise
self._already_slept = True
sleep(1000)
xbmc.sleep(1000)
json_item = js.get_item(playerid)
LOG.debug('Kodi playing item properties: %s', json_item)
return (json_item.get('id'),
@ -424,16 +425,130 @@ class SpecialMonitor(Thread):
def run(self):
LOG.info("----====# Starting Special Monitor #====----")
# "Start from beginning", "Play from beginning"
strings = (try_encode(getLocalizedString(12021)),
try_encode(getLocalizedString(12023)))
strings = (try_encode(xbmc.getLocalizedString(12021)),
try_encode(xbmc.getLocalizedString(12023)))
while not self.stopped():
if getCondVisibility('Window.IsVisible(DialogContextMenu.xml)'):
if getInfoLabel('Control.GetLabel(1002)') in strings:
if xbmc.getCondVisibility('Window.IsVisible(DialogContextMenu.xml)'):
if xbmc.getInfoLabel('Control.GetLabel(1002)') in strings:
# Remember that the item IS indeed resumable
control = int(Window(10106).getFocusId())
state.RESUME_PLAYBACK = True if control == 1001 else False
else:
# Different context menu is displayed
state.RESUME_PLAYBACK = False
sleep(200)
xbmc.sleep(200)
LOG.info("#====---- Special Monitor Stopped ----====#")
@LOCKER.lockthis
def _playback_cleanup(ended=False):
"""
PKC cleanup after playback ends/is stopped. Pass ended=True if Kodi
completely finished playing an item (because we will get and use wrong
timing data otherwise)
"""
LOG.debug('playback_cleanup called. Active players: %s',
state.ACTIVE_PLAYERS)
# We might have saved a transient token from a user flinging media via
# Companion (if we could not use the playqueue to store the token)
state.PLEX_TRANSIENT_TOKEN = None
for playerid in state.ACTIVE_PLAYERS:
status = state.PLAYER_STATES[playerid]
# Remember the last played item later
state.OLD_PLAYER_STATES[playerid] = copy.deepcopy(status)
# Stop transcoding
if status['playmethod'] == 'Transcode':
LOG.debug('Tell the PMS to stop transcoding')
DU().downloadUrl(
'{server}/video/:/transcode/universal/stop',
parameters={'session': v.PKC_MACHINE_IDENTIFIER})
if playerid == 1:
# Bookmarks might not be pickup up correctly, so let's do them
# manually. Applies to addon paths, but direct paths might have
# started playback via PMS
_record_playstate(status, ended)
# Reset the player's status
state.PLAYER_STATES[playerid] = copy.deepcopy(state.PLAYSTATE)
# As all playback has halted, reset the players that have been active
state.ACTIVE_PLAYERS = []
LOG.debug('Finished PKC playback cleanup')
def _record_playstate(status, ended):
if not status['plex_id']:
LOG.debug('No Plex id found to record playstate for status %s', status)
return
with plexdb.Get_Plex_DB() as plex_db:
kodi_db_item = plex_db.getItem_byId(status['plex_id'])
if kodi_db_item is None:
# Item not (yet) in Kodi library
LOG.debug('No playstate update due to Plex id not found: %s', status)
return
totaltime = float(kodi_time_to_millis(status['totaltime'])) / 1000
if ended:
progress = 0.99
time = v.IGNORE_SECONDS_AT_START + 1
else:
time = float(kodi_time_to_millis(status['time'])) / 1000
try:
progress = time / totaltime
except ZeroDivisionError:
progress = 0.0
LOG.debug('Playback progress %s (%s of %s seconds)',
progress, time, totaltime)
playcount = status['playcount']
last_played = unix_date_to_kodi(unix_timestamp())
if playcount is None:
LOG.debug('playcount not found, looking it up in the Kodi DB')
with kodidb.GetKodiDB('video') as kodi_db:
playcount = kodi_db.get_playcount(kodi_db_item[1])
playcount = 0 if playcount is None else playcount
if time < v.IGNORE_SECONDS_AT_START:
LOG.debug('Ignoring playback less than %s seconds',
v.IGNORE_SECONDS_AT_START)
# Annoying Plex bug - it'll reset an already watched video to unwatched
playcount = None
last_played = None
time = 0
elif progress >= v.MARK_PLAYED_AT:
LOG.debug('Recording entirely played video since progress > %s',
v.MARK_PLAYED_AT)
playcount += 1
time = 0
with kodidb.GetKodiDB('video') as kodi_db:
kodi_db.addPlaystate(kodi_db_item[1],
time,
totaltime,
playcount,
last_played)
# Hack to force "in progress" widget to appear if it wasn't visible before
if (state.FORCE_RELOAD_SKIN and
xbmc.getCondVisibility('Window.IsVisible(Home.xml)')):
LOG.debug('Refreshing skin to update widgets')
xbmc.executebuiltin('ReloadSkin()')
thread = Thread(target=_clean_file_table)
thread.setDaemon(True)
thread.start()
def _clean_file_table():
"""
If we associate a playing video e.g. pointing to plugin://... to an existing
Kodi library item, Kodi will add an additional entry for this (additional)
path plugin:// in the file table. This leads to all sorts of wierd behavior.
This function tries for at most 5 seconds to clean the file table.
"""
LOG.debug('Start cleaning Kodi files table')
i = 0
while i < 100 and not state.STOP_PKC:
with kodidb.GetKodiDB('video') as kodi_db:
files = kodi_db.obsolete_file_ids()
if files:
break
i += 1
xbmc.sleep(50)
with kodidb.GetKodiDB('video') as kodi_db:
for file_id in files:
LOG.debug('Removing obsolete Kodi file_id %s', file_id)
kodi_db.remove_file(file_id[0], remove_orphans=False)
LOG.debug('Done cleaning up Kodi file table')

View file

@ -49,10 +49,11 @@ def playback_triage(plex_id=None, plex_type=None, path=None, resolve=True):
the first pass - e.g. if you're calling this function from the original
service.py Python instance
"""
LOG.info('playback_triage called with plex_id %s, plex_type %s, path %s',
plex_id, plex_type, path)
LOG.info('playback_triage called with plex_id %s, plex_type %s, path %s, '
'resolve %s,', plex_id, plex_type, path, resolve)
global RESOLVE
RESOLVE = resolve
# If started via Kodi context menu, we never resolve
RESOLVE = resolve if not state.CONTEXT_MENU_PLAY else False
if not state.AUTHENTICATED:
LOG.error('Not yet authenticated for PMS, abort starting playback')
# "Unauthorized for PMS"
@ -64,7 +65,7 @@ def playback_triage(plex_id=None, plex_type=None, path=None, resolve=True):
pos = js.get_position(playqueue.playlistid)
# Can return -1 (as in "no playlist")
pos = pos if pos != -1 else 0
LOG.debug('playQueue position: %s for %s', pos, playqueue)
LOG.debug('playQueue position %s for %s', pos, playqueue)
# Have we already initiated playback?
try:
item = playqueue.items[pos]
@ -98,7 +99,7 @@ def _playback_init(plex_id, plex_type, playqueue, pos):
try:
_init_existing_kodi_playlist(playqueue, pos)
except PL.PlaylistError:
LOG.error('Aborting playback_init for longer Kodi playlist')
LOG.error('Playback_init for existing Kodi playlist failed')
_ensure_resolve(abort=True)
return
# Now we need to use setResolvedUrl for the item at position ZERO
@ -107,7 +108,8 @@ def _playback_init(plex_id, plex_type, playqueue, pos):
return
# "Usual" case - consider trailers and parts and build both Kodi and Plex
# playqueues
# Fail the item we're trying to play now so we can restart the player
# Pass dummy PKC video with 0 length so Kodi immediately stops playback
# and we can build our own playqueue.
_ensure_resolve()
api = API(xml[0])
trailers = False
@ -120,6 +122,10 @@ def _playback_init(plex_id, plex_type, playqueue, pos):
else:
trailers = True
LOG.debug('Playing trailers: %s', trailers)
if RESOLVE:
# Sleep a bit to let setResolvedUrl do its thing - bit ugly
while not state.PKC_CAUSED_STOP_DONE:
sleep(50)
playqueue.clear()
if plex_type != v.PLEX_TYPE_CLIP:
# Post to the PMS to create a playqueue - in any case due to Companion
@ -132,13 +138,13 @@ def _playback_init(plex_id, plex_type, playqueue, pos):
plex_id, xml.attrib.get('librarySectionUUID'))
# "Play error"
dialog('notification', lang(29999), lang(30128), icon='{error}')
_ensure_resolve(abort=True)
# Do NOT use _ensure_resolve() because we resolved above already
state.CONTEXT_MENU_PLAY = False
state.FORCE_TRANSCODE = False
state.RESUME_PLAYBACK = False
return
# Should already be empty, but just in case
PL.get_playlist_details_from_xml(playqueue, xml)
stack = _prep_playlist_stack(xml)
# Sleep a bit to let setResolvedUrl do its thing - bit ugly
sleep(200)
_process_stack(playqueue, stack)
# Always resume if playback initiated via PMS and there IS a resume
# point
@ -146,7 +152,6 @@ def _playback_init(plex_id, plex_type, playqueue, pos):
# Reset some playback variables
state.CONTEXT_MENU_PLAY = False
state.FORCE_TRANSCODE = False
# Do NOT set offset, because Kodi player will return here to resolveURL
# New thread to release this one sooner (e.g. harddisk spinning up)
thread = Thread(target=threaded_playback,
args=(playqueue.kodi_pl, pos, offset))
@ -160,6 +165,8 @@ def _playback_init(plex_id, plex_type, playqueue, pos):
# plugin://pkc will be lost; Kodi will try to startup playback for an empty
# path: log entry is "CGUIWindowVideoBase::OnPlayMedia <missing path>"
thread.start()
# Ensure that PKC playqueue monitor ignores the changes we just made
playqueue.pkc_edit = True
def _ensure_resolve(abort=False):
@ -173,9 +180,10 @@ def _ensure_resolve(abort=False):
"""
if RESOLVE:
LOG.debug('Passing dummy path to Kodi')
if not state.CONTEXT_MENU_PLAY:
# Because playback won't start with context menu play
state.PKC_CAUSED_STOP = True
# if not state.CONTEXT_MENU_PLAY:
# Because playback won't start with context menu play
state.PKC_CAUSED_STOP = True
state.PKC_CAUSED_STOP_DONE = False
result = Playback_Successful()
result.listitem = PKC_ListItem(path=NULL_VIDEO)
pickle_me(result)
@ -345,7 +353,8 @@ def process_indirect(key, offset, resolve=True):
Set resolve to False if playback should be kicked off directly, not via
setResolvedUrl
"""
LOG.info('process_indirect called with key: %s, offset: %s', key, offset)
LOG.info('process_indirect called with key: %s, offset: %s, resolve: %s',
key, offset, resolve)
global RESOLVE
RESOLVE = resolve
result = Playback_Successful()

View file

@ -1,156 +0,0 @@
# -*- coding: utf-8 -*-
###############################################################################
from logging import getLogger
import copy
import xbmc
import kodidb_functions as kodidb
import plexdb_functions as plexdb
from downloadutils import DownloadUtils as DU
from plexbmchelper.subscribers import LOCKER
from utils import kodi_time_to_millis, unix_date_to_kodi, unix_timestamp
import variables as v
import state
###############################################################################
LOG = getLogger("PLEX." + __name__)
###############################################################################
@LOCKER.lockthis
def playback_cleanup(ended=False):
"""
PKC cleanup after playback ends/is stopped. Pass ended=True if Kodi
completely finished playing an item (because we will get and use wrong
timing data otherwise)
"""
LOG.debug('playback_cleanup called')
# We might have saved a transient token from a user flinging media via
# Companion (if we could not use the playqueue to store the token)
state.PLEX_TRANSIENT_TOKEN = None
for playerid in state.ACTIVE_PLAYERS:
status = state.PLAYER_STATES[playerid]
# Remember the last played item later
state.OLD_PLAYER_STATES[playerid] = copy.deepcopy(status)
# Stop transcoding
if status['playmethod'] == 'Transcode':
LOG.debug('Tell the PMS to stop transcoding')
DU().downloadUrl(
'{server}/video/:/transcode/universal/stop',
parameters={'session': v.PKC_MACHINE_IDENTIFIER})
if playerid == 1:
# Bookmarks might not be pickup up correctly, so let's do them
# manually. Applies to addon paths, but direct paths might have
# started playback via PMS
_record_playstate(status, ended)
# Reset the player's status
state.PLAYER_STATES[playerid] = copy.deepcopy(state.PLAYSTATE)
# As all playback has halted, reset the players that have been active
state.ACTIVE_PLAYERS = []
LOG.debug('Finished PKC playback cleanup')
def _record_playstate(status, ended):
if not status['plex_id']:
LOG.debug('No Plex id found to record playstate for status %s', status)
return
with plexdb.Get_Plex_DB() as plex_db:
kodi_db_item = plex_db.getItem_byId(status['plex_id'])
if kodi_db_item is None:
# Item not (yet) in Kodi library
LOG.debug('No playstate update due to Plex id not found: %s', status)
return
totaltime = float(kodi_time_to_millis(status['totaltime'])) / 1000
if ended:
progress = 0.99
time = v.IGNORE_SECONDS_AT_START + 1
else:
time = float(kodi_time_to_millis(status['time'])) / 1000
try:
progress = time / totaltime
except ZeroDivisionError:
progress = 0.0
LOG.debug('Playback progress %s (%s of %s seconds)',
progress, time, totaltime)
playcount = status['playcount']
last_played = unix_date_to_kodi(unix_timestamp())
if playcount is None:
LOG.debug('playcount not found, looking it up in the Kodi DB')
with kodidb.GetKodiDB('video') as kodi_db:
playcount = kodi_db.get_playcount(kodi_db_item[1])
playcount = 0 if playcount is None else playcount
if time < v.IGNORE_SECONDS_AT_START:
LOG.debug('Ignoring playback less than %s seconds',
v.IGNORE_SECONDS_AT_START)
# Annoying Plex bug - it'll reset an already watched video to unwatched
playcount = None
last_played = None
time = 0
elif progress >= v.MARK_PLAYED_AT:
LOG.debug('Recording entirely played video since progress > %s',
v.MARK_PLAYED_AT)
playcount += 1
time = 0
with kodidb.GetKodiDB('video') as kodi_db:
kodi_db.addPlaystate(kodi_db_item[1],
time,
totaltime,
playcount,
last_played)
# Hack to force "in progress" widget to appear if it wasn't visible before
if (state.FORCE_RELOAD_SKIN and
xbmc.getCondVisibility('Window.IsVisible(Home.xml)')):
LOG.debug('Refreshing skin to update widgets')
xbmc.executebuiltin('ReloadSkin()')
class PKC_Player(xbmc.Player):
def __init__(self):
xbmc.Player.__init__(self)
LOG.info("Started playback monitor.")
def onPlayBackStarted(self):
"""
Will be called when xbmc starts playing a file.
"""
pass
def onPlayBackPaused(self):
"""
Will be called when playback is paused
"""
pass
def onPlayBackResumed(self):
"""
Will be called when playback is resumed
"""
pass
def onPlayBackSeek(self, time, seekOffset):
"""
Will be called when user seeks to a certain time during playback
"""
pass
def onPlayBackStopped(self):
"""
Will be called when playback is stopped by the user
"""
LOG.debug("ONPLAYBACK_STOPPED")
playback_cleanup()
def onPlayBackEnded(self):
"""
Will be called when playback ends due to the media file being finished
"""
LOG.debug("ONPLAYBACK_ENDED")
if state.PKC_CAUSED_STOP is True:
state.PKC_CAUSED_STOP = False
LOG.debug('PKC caused this playback stop - ignoring')
else:
playback_cleanup(ended=True)

View file

@ -49,6 +49,9 @@ class PlaylistObjectBaseclase(object):
self.plex_transient_token = None
# Need a hack for detecting swaps of elements
self.old_kodi_pl = []
# Did PKC itself just change the playqueue so the PKC playqueue monitor
# should not pick up any changes?
self.pkc_edit = False
# Workaround to avoid endless loops of detecting PL clears
self._clear_list = []
@ -64,6 +67,8 @@ class PlaylistObjectBaseclase(object):
if isinstance(getattr(self, key), str):
answ += '\'%s\': \'%s\', ' % (key,
try_decode(getattr(self, key)))
elif isinstance(getattr(self, key), unicode):
answ += '\'%s\': \'%s\', ' % (key, getattr(self, key))
else:
# e.g. int
answ += '\'%s\': %s, ' % (key, unicode(getattr(self, key)))
@ -184,6 +189,8 @@ class Playlist_Item(object):
if isinstance(getattr(self, key), str):
answ += '\'%s\': \'%s\', ' % (key,
try_decode(getattr(self, key)))
elif isinstance(getattr(self, key), unicode):
answ += '\'%s\': \'%s\', ' % (key, getattr(self, key))
else:
# e.g. int
answ += '\'%s\': %s, ' % (key, unicode(getattr(self, key)))

View file

@ -226,17 +226,26 @@ class PlayqueueMonitor(Thread):
if stopped():
break
sleep(1000)
for playqueue in PLAYQUEUES:
kodi_pl = js.playlist_get_items(playqueue.playlistid)
if playqueue.old_kodi_pl != kodi_pl:
if playqueue.id is None and (not state.DIRECT_PATHS or
state.CONTEXT_MENU_PLAY):
# Only initialize if directly fired up using direct
# paths. Otherwise let default.py do its magic
LOG.debug('Not yet initiating playback')
else:
# compare old and new playqueue
self._compare_playqueues(playqueue, kodi_pl)
playqueue.old_kodi_pl = list(kodi_pl)
work = []
# Detect changed playqueues first, do the work afterwards
with LOCK:
for playqueue in PLAYQUEUES:
kodi_pl = js.playlist_get_items(playqueue.playlistid)
if playqueue.old_kodi_pl != kodi_pl:
if playqueue.id is None and (not state.DIRECT_PATHS or
state.CONTEXT_MENU_PLAY):
# Only initialize if directly fired up using direct
# paths. Otherwise let default.py do its magic
LOG.debug('Not yet initiating playback')
elif playqueue.pkc_edit:
playqueue.pkc_edit = False
LOG.debug('PKC edited the playqueue - skipping')
else:
# We do need to update our playqueues
work.append((playqueue, kodi_pl))
playqueue.old_kodi_pl = kodi_pl
# Now do the work - LOCK individual playqueue edits
for playqueue, kodi_pl in work:
self._compare_playqueues(playqueue, kodi_pl)
sleep(200)
LOG.info("----===## PlayqueueMonitor stopped ##===----")

View file

@ -1,17 +1,12 @@
# -*- coding: utf-8 -*-
###############################################################################
from logging import getLogger
from utils import kodi_sql
import variables as v
###############################################################################
log = getLogger("PLEX."+__name__)
###############################################################################
class Get_Plex_DB():
"""
@ -183,34 +178,15 @@ class Plex_DB_Functions():
None if not found
"""
query = '''
SELECT plex_id
FROM plex
WHERE kodi_fileid = ? AND kodi_type = ?
SELECT plex_id FROM plex WHERE kodi_fileid = ? AND kodi_type = ?
LIMIT 1
'''
self.plexcursor.execute(query, (kodi_fileid, kodi_type))
try:
self.plexcursor.execute(query, (kodi_fileid, kodi_type))
item = self.plexcursor.fetchone()[0]
return item
except:
return None
def getMusicItem_byFileId(self, kodi_id, kodi_type):
"""
Returns the plex_id for kodi_id and kodi_type
None if not found
"""
query = '''
SELECT plex_id
FROM plex
WHERE kodi_id = ? AND kodi_type = ?
'''
try:
self.plexcursor.execute(query, (kodi_id, kodi_type))
item = self.plexcursor.fetchone()[0]
return item
except:
return None
except TypeError:
item = None
return item
def getItem_byId(self, plex_id):
"""
@ -237,7 +213,7 @@ class Plex_DB_Functions():
FROM plex
WHERE plex_id LIKE ?
'''
self.plexcursor.execute(query, (plex_id+"%",))
self.plexcursor.execute(query, (plex_id + "%",))
return self.plexcursor.fetchall()
def getItem_byView(self, view_id):
@ -260,8 +236,7 @@ class Plex_DB_Functions():
query = '''
SELECT plex_id, parent_id, plex_type
FROM plex
WHERE kodi_id = ?
AND kodi_type = ?
WHERE kodi_id = ? AND kodi_type = ?
'''
self.plexcursor.execute(query, (kodi_id, kodi_type,))
return self.plexcursor.fetchone()
@ -304,24 +279,6 @@ class Plex_DB_Functions():
self.plexcursor.execute(query, (plex_type,))
return self.plexcursor.fetchall()
def getMediaType_byId(self, plex_id):
"""
Returns plex_type for plex_id
Or None if not found
"""
query = '''
SELECT plex_type
FROM plex
WHERE plex_id = ?
'''
self.plexcursor.execute(query, (plex_id,))
try:
itemtype = self.plexcursor.fetchone()[0]
except TypeError:
itemtype = None
return itemtype
def addReference(self, plex_id, plex_type, kodi_id, kodi_type,
kodi_fileid=None, kodi_pathid=None, parent_id=None,
checksum=None, view_id=None):
@ -381,13 +338,6 @@ class Plex_DB_Functions():
self.plexcursor.execute('DELETE FROM plex WHERE plex_id = ?',
(plex_id,))
def removeWildItem(self, plex_id):
"""
Removes all entries with plex_id with % added
"""
query = "DELETE FROM plex WHERE plex_id LIKE ?"
self.plexcursor.execute(query, (plex_id+"%",))
def itemsByType(self, plex_type):
"""
Returns a list of dicts for plex_type:
@ -418,7 +368,7 @@ class Plex_DB_Functions():
"""
Sets the fanart_synced flag to 1 for plex_id
"""
query = '''UPDATE plex SET fanart_synced = 1 WHERE plex_id = ?'''
query = 'UPDATE plex SET fanart_synced = 1 WHERE plex_id = ?'
self.plexcursor.execute(query, (plex_id,))
def get_missing_fanart(self):

View file

@ -95,8 +95,12 @@ WEBSOCKET_QUEUE = None
# Which Kodi player is/has been active? (either int 1, 2 or 3)
ACTIVE_PLAYERS = []
# Failsafe for throwing failing ListItems() back to Kodi's setResolvedUrl
# Failsafe for throwing an empty video back to Kodi's setResolvedUrl to set
# up our own playlist from the very beginning
PKC_CAUSED_STOP = False
# Flag if the 0 length PKC video has already failed so we can start resolving
# playback (set in player.py)
PKC_CAUSED_STOP_DONE = True
# Kodi player states - here, initial values are set
PLAYER_STATES = {