Merge pull request #1482 from croneter/python3-beta

Bump python3 branch
This commit is contained in:
croneter 2021-05-23 08:01:40 +02:00 committed by GitHub
commit 84d677b7a2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
54 changed files with 7237 additions and 958 deletions

View file

@ -81,6 +81,7 @@ Some people argue that PKC is 'hacky' because of the way it directly accesses th
+ Hungarian, thanks @savage93 + Hungarian, thanks @savage93
+ Ukrainian, thanks @uniss + Ukrainian, thanks @uniss
+ Lithuanian, thanks @egidusm + Lithuanian, thanks @egidusm
+ Korean, thanks @so-o-bima
### Additional Artwork ### Additional Artwork
PKC uses additional artwork for free from [TheMovieDB](https://www.themoviedb.org). Many thanks for lettings us use the API, guys! PKC uses additional artwork for free from [TheMovieDB](https://www.themoviedb.org). Many thanks for lettings us use the API, guys!

913
addon.xml
View file

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<addon id="plugin.video.plexkodiconnect" name="PlexKodiConnect" version="3.1.0" provider-name="croneter"> <addon id="plugin.video.plexkodiconnect" name="PlexKodiConnect" version="3.2.0" provider-name="croneter">
<requires> <requires>
<import addon="xbmc.python" version="3.0.0"/> <import addon="xbmc.python" version="3.0.0"/>
<import addon="script.module.requests" version="2.22.0+matrix.1" /> <import addon="script.module.requests" version="2.22.0+matrix.1" />
@ -83,13 +83,41 @@
<summary lang="uk_UA">Нативна інтеграція Plex в Kodi</summary> <summary lang="uk_UA">Нативна інтеграція Plex в Kodi</summary>
<description lang="uk_UA">Підключає Kodi до серверу Plex. Цей плагін передбачає, що ви керуєте всіма своїми відео за допомогою Plex (і ніяк не Kodi). Ви можете втратити дані, які вже зберігаються у відео та музичних БД Kodi (оскільки цей плагін безпосередньо їх змінює). Використовуйте на свій страх і ризик!</description> <description lang="uk_UA">Підключає Kodi до серверу Plex. Цей плагін передбачає, що ви керуєте всіма своїми відео за допомогою Plex (і ніяк не Kodi). Ви можете втратити дані, які вже зберігаються у відео та музичних БД Kodi (оскільки цей плагін безпосередньо їх змінює). Використовуйте на свій страх і ризик!</description>
<disclaimer lang="uk_UA">Використовуйте на свій ризик</disclaimer> <disclaimer lang="uk_UA">Використовуйте на свій ризик</disclaimer>
<disclaimer lang="lv_LV">Lieto uz savu atbildību</disclaimer>
<summary lang="sv_SE">Inbyggd integrering av Plex i Kodi</summary> <summary lang="sv_SE">Inbyggd integrering av Plex i Kodi</summary>
<description lang="sv_SE">Anslut Kodi till din Plex Media Server. Detta tillägg antar att du hanterar alla dina filmer med Plex (och ingen med Kodi). Du kan förlora data redan sparad i Kodis video och musik databaser (eftersom detta tillägg direkt ändrar dem). Använd på egen risk!</description> <description lang="sv_SE">Anslut Kodi till din Plex Media Server. Detta tillägg antar att du hanterar alla dina filmer med Plex (och ingen med Kodi). Du kan förlora data redan sparad i Kodis video och musik databaser (eftersom detta tillägg direkt ändrar dem). Använd på egen risk!</description>
<disclaimer lang="sv_SE">Använd på egen risk</disclaimer> <disclaimer lang="sv_SE">Använd på egen risk</disclaimer>
<summary lang="lt_LT">Natūralioji „Plex“ integracija į „Kodi“</summary> <summary lang="lt_LT">Natūralioji „Plex“ integracija į „Kodi“</summary>
<description lang="lt_LT">Prijunkite „Kodi“ prie „Plex Medija Serverio“. Šiame papildinyje daroma prielaida, kad valdote visus savo vaizdo įrašus naudodami „Plex“ (ir nė vieno su „Kodi“). Galite prarasti jau saugomus „Kodi“ vaizdo įrašų ir muzikos duomenų bazių duomenis (kadangi šis papildinys juos tiesiogiai pakeičia). Naudokite savo pačių rizika!</description> <description lang="lt_LT">Prijunkite „Kodi“ prie „Plex Medija Serverio“. Šiame papildinyje daroma prielaida, kad valdote visus savo vaizdo įrašus naudodami „Plex“ (ir nė vieno su „Kodi“). Galite prarasti jau saugomus „Kodi“ vaizdo įrašų ir muzikos duomenų bazių duomenis (kadangi šis papildinys juos tiesiogiai pakeičia). Naudokite savo pačių rizika!</description>
<disclaimer lang="lt_LT">Naudokite savo pačių rizika</disclaimer> <disclaimer lang="lt_LT">Naudokite savo pačių rizika</disclaimer>
<news>version 3.1.0: <summary lang="ko_KR">Plex를 Kodi에 기본 통합</summary>
<description lang="ko_KR">Kodi를 Plex Media Server에 연결합니다. 이 플러그인은 Plex로 모든 비디오를 관리하고 Kodi로는 관리하지 않는다고 가정합니다. Kodi 비디오 및 음악 데이터베이스에 이미 저장된 데이터가 손실 될 수 있습니다 (이 플러그인이 직접 변경하므로). 자신의 책임하에 사용하십시오!</description>
<disclaimer lang="ko_KR">자신의 책임하에 사용</disclaimer>
<news>version 3.2.0:
WARNING: Database reset and full resync required
- version 3.1.1-3.1.4 for everyone
version 3.1.4 (beta only):
- Fix Alexa and RuntimeError: dictionary keys changed during iteration
- Fix AttributeError: module 'shutil' has no attribute 'copy_tree'
version 3.1.3 (beta only):
- Add PKC setting to disable verification whether we can access a media file
- Direct paths: corrections to more closely mirror Kodi's way of saving movie and tv show files to the db
- Make sure that the correct file system encoding is used for playlists
- Fix a rare AttributeError when using playlists
- Fix regression: fix add-on paths always falling back to direct paths
version 3.1.2 (beta only):
- Fix ImportError: cannot import name 'dir_util' from 'distutils' on PKC startup
- Fix UnicodeEncodeError if Plex playlist name contains illegal chars
- Fix PKC not showing up as a casting target in some cases
version 3.1.1 (beta only):
- Direct paths: fix filename showing instead of full video metadata during playback
- Update translations
version 3.1.0:
- version 3.0.16 and 3.0.17 for everyone - version 3.0.16 and 3.0.17 for everyone
- Fix resume not working if Kodi player start-up is slow - Fix resume not working if Kodi player start-up is slow
@ -163,885 +191,6 @@ version 3.0.1:
version 3.0.0: version 3.0.0:
- Major upgrade from Python 2 to Python 3, allowing use of Kodi 19 Matrix - Major upgrade from Python 2 to Python 3, allowing use of Kodi 19 Matrix
</news>
version 2.12.18 (beta only):
- Quickly sync recently watched items before synching the playstates of the entire Plex library
- Improve logging for websocket JSON loads
version 2.12.17 (beta only):
- Sync name and user rating of a TV show season to Kodi
- Fix rare TypeError: expected string or buffer on playback start
version 2.12.16:
- versions 2.12.14 and 2.12.15 for everyone
version 2.12.15 (beta only):
- Fix skip intros sometimes not working due to a RuntimeError
- Update translations
version 2.12.14 (beta only):
- Add skip intro functionality
version 2.12.13:
- Fix KeyError: u'game' if Plex Arcade has been activated
- Fix AttributeError: 'App' object has no attribute 'threads' when sync is cancelled
version 2.12.12:
- Hopefully fix rare case when sync would get stuck indefinitely
- Fix ValueError: invalid literal for int() for invalid dates sent by Plex
- version 2.12.11 for everyone
version 2.12.11 (beta only):
- Fix PKC not auto-picking audio/subtitle stream when transcoding
- Fix ValueError when deleting a music album
- Fix OSError: Invalid argument when Plex returns an invalid timestamp
version 2.12.10:
- Fix pictures from Plex picture libraries not working/displaying
version 2.12.9:
- Fix Local variable 'user' referenced before assignement
version 2.12.8:
- version 2.12.7 for everyone
version 2.12.7 (beta only):
- Fix PKC suddenly using main Plex user's credentials, e.g. when the PMS address changed
- Fix missing Kodi tags for movie collections/sets
version 2.12.6:
- Fix rare KeyError when using PKC widgets
- Fix suspension of artwork caching and PKC becoming unresponsive
- Update translations
- Versions 2.12.4 and 2.12.5 for everyone
version 2.12.5 (beta only):
- Greatly improve matching logic for The Movie Database if Plex does not provide an appropriate id
- Fix high transcoding resolutions not being available for Win10
- Fix rare playback progress report failing and KeyError: u'containerKey'
- Fix rare KeyError: None when trying to sync playlists
- Fix TypeError when canceling Plex sync section dialog
version 2.12.4 (beta only):
- Hopefully fix freeze during sync: Don't assign multiple sets/collections for a specific movie
- Support metadata provider ids (e.g. for IMDB) for the new Plex Movie Agent
version 2.12.3:
- Fix playback failing due to caching of subtitles with non-ascii chars
- Fix ValueError: invalid literal for int() with base 10 during show sync
- Fix UnboundLocalError when certain Plex sections are deleted or being un-synched
- New method to install PlexKodiConnect directly via an URL. You thus do not need to upload a ZIP file to Kodi anymore.
version 2.12.2:
- version 2.12.0 and 2.12.1 for everyone
- Fix regression: sync dialog not showing up when it should
version 2.12.1 (beta only):
- Fix PKC shutdown on Kodi profile switch
- Fix Kodi content type for images/photos
- Added support for custom set of safe characters when escaping paths (thanks @geropan)
- Revert "Don't allow spaces in devicename"
- Fix sync dialog showing in certain cases even though user opted out
version 2.12.0 (beta only):
- Fix websocket threads; enable PKC background sync for all Plex Home users!
- Fix PKC incorrectly marking a video as unwatched if an external player has been used
- Update translations
version 2.11.7:
- Fix PKC crashing on devices running Microsoft UWP, e.g. XBox
version 2.11.6:
- Fix rare sync crash when queue was full
- Set "Auto-adjust transcoding quality" to false by default
version 2.11.5:
- Versions 2.11.0-2.11.4 for everyone
version 2.11.4 (beta only):
- Fix another TypeError: 'NoneType' object has no attribute '__getitem__', e.g. when trying to play trailers
version 2.11.3 (beta only):
- Fix TypeError: 'NoneType' object has no attribute '__getitem__', e.g. when displaying albums
version 2.11.2 (beta only):
- Refactor direct and add-on paths. Enables use of Plex music playlists synched to Kodi
version 2.11.1 (beta only):
- Rewire the set-up of audio and subtitle streams, esp. before starting a transcoding session. Fixes playback not starting at all
version 2.11.0 (beta only):
- Fix PKC not burning in (and thus not showing) subtitles when transcoding
- When transcoding, only let user choose to burn-in subtitles that can't be displayed otherwise by Kodi
- Improve PKC automatically connecting to local PMS
- Ensure that our only video transcoding target is h264
- Fix adjusted subtitle size not working when burning in subtitles
- Fix regression: burn-in subtitles picking up the last user setting instead of the current one
version 2.10.12:
- versions 2.10.5-11 for everyone
version 2.10.11 (beta only):
- Fix yet another rare but annoying bug where PKC becomes unresponsive during sync
version 2.10.10 (beta only):
- Fix rare but annoying bug where PKC becomes unresponsive during sync
- Fix PKC background sync not working in some cases
version 2.10.9 (beta only):
- Other Kodi add-ons can now search for Plex items using plugin://plugin.video.plexkodiconnect?mode=search&amp;query=YOUR SEARCH STRING HERE
version 2.10.8 (beta only):
- Improve thread pool management to render PKC snappier
- Attempt to fix broken pipe error
- Fix DirectPaths when a video's folder name is identical to a video's filename (you will need to manually reset the Kodi database)
version 2.10.7 (beta only):
- Fix PKC not starting up on iOS
- Optimize the new sync process and fix some bugs that were introduced
- Fix PKC becoming unresponsive e.g. when switching the PMS
version 2.10.6 (beta only):
- Fix AttributeError if user enters an invalid pin code
- Fix OperationalError when starting with a fresh PKC installation
- Fix IndexError
version 2.10.5 (beta only):
- Rewire library sync to speed it up and fix sync getting stuck in rare cases
- Optimize threads by using events instead of a polling mechanism. Fixes PKC becoming unresponsive, e.g. when switching users
- Optimize adding values to Kodi databases by not using sqlite COALESCE command
- Fix OperationalError when resetting PKC
- Improve sync resiliance when certain items are not to be synced to Kodi or PKC skipped an item in the past
- Make sure bool is returned instead of an int
- Don't use WAL mode for sqlite connections, it is not making any difference
version 2.10.4:
- version 2.10.3 for everyone
- Fix to correctly wipe Kodi databases
version 2.10.3 (beta only):
- Fix a couple of issues with music when using direct paths: correctly escape music paths for Kodi regex matching
- Fix Recently Added Albums sort order (you will have to reset the Kodi database manually)
- Fix database being locked in rare cases
- Increase batch size for library sync from 500 to 2000 to increase sync speed
- Optimize some code
- Fix KeyError when using Plex search capabilities
- Check faster for available Plex Media Server to connect to
version 2.10.2:
- Fix Kodi playback jumping to the beginning of a video that just started
- Fix transcoding quality degenerating quickly while playing with a new setting to deactivate auto quality for transcoding (applicable e.g. for Chromecast)
- Update translations
version 2.10.1:
- Fix resume for Kodi on low powered devices, e.g. Raspberry Pi
- Fix resume when using an external player
- Fix UnicodeWarning: Unicode equal comparison failed to convert both arguments to Unicode - interpreting them as being unequal
version 2.10.0:
- version 2.9.12 - 2.9.14 for everyone
- Get rid of some obsolete code for the ContextMonitor we dropped
version 2.9.14 (beta only):
- Fix resume when starting playback via PMS or when force transcoding
- Get rid of ContextMonitor and the dedicated Python thread - with new resume mechanics, this is not needed anymore
- Optimize clean-up of file table in the Kodi video database after stopping playback
- Get rid of some obsolete imports
version 2.9.13 (beta only):
- Fix PKC resuming instead of playing from the beginning
version 2.9.12 (beta only):
- Fix resume not working in some cases
- Support Plex search across all media and Plex Media Servers: Navigate to the PlexKodiConnect Add-on, then "Search"
- Always use the current Kodi language when communicating with the PMS (restart Kodi when changing the language!)
- Fix Kodi crashing when casting from e.g. Plex Web or Plex for Windows
- Fix PKC throwing error if m3u playlist contains resume information
version 2.9.11:
- version 2.9.10 for everyone
version 2.9.10 (beta only):
- Add tmdb provider sync
- Fix external subtitles not being available
- Fix PKC increasing the Plex watch count by 2 instead of 1
- Improve subtitle naming
- Delete temporary subtitles on playback stop
- Fix a missleading string
version 2.9.9:
- Versions 2.9.6 - 2.9.8 for everyone
version 2.9.8 (beta only):
- Fix Play Error in scenarios (older PMS version?) where posting playqueues using an uri `server://` is not possible and `library://` is necessary
- Fix rare AttributeError on PKC startup when modifying advancedsettings.xml
- Update translations
version 2.9.7 (beta only):
- Correctly escape URLs for Direct Paths
- Update settings to inform user that reboot is necessary
- Optimize code
- Don't migrate PKC settings if we're dealing with a clean new PKC installation
- Force-scan every single item in the library - seems like we could lose some recently added items otherwise when updating PKC
version 2.9.6 (beta only):
- Rework logic for using direct paths, direct play, direct streaming and transcoding, using the PMS StreamingBrain: Let PMS StreamingBrain decide on whether we need to force-transcode, New setting to choose "Direct Streaming", Allow for 4k transcoding and direct streaming, New setting to force transcode only 4K and above
- Fix PKC background sync synching items to Kodi even though entire section should not be synched
- Force a full sync of all items after choosing a new PMS, changing a PMS' address and changing which Plex libraries to sync
- Only enforce advancedsettings.xml 'cleanonupdate' to be false for PKC add-on paths
- Never give up trying to connect to the PMS or Alexa using websockets
- Fix resume when force-transcoding
version 2.9.5:
- Version 2.9.4 for everyone
version 2.9.4 (beta only):
- Fix extras not playing when path substitution is enabled
- Fix Plex Companion device restarting playback when reconnecting to PKC
- Fix playback report not working after having played a non-Plex video file
- Change how items are added to Plex playqueues by using PMS machine identifier
- Optimize code for playqueue items
- Fix rare AttributeError when shutting down Kodi
version 2.9.3:
- version 2.9.2 for everyone
version 2.9.2 (beta only):
- Fix Plex Companion casting from iOS and Android
- Faster sync of playlists
- Sync playlists immediately after synching new/changed items and show an info dialog
- Fix potential playlist sync issues if there is a dot in the playlist name
- Correctly detect whether we already synched a Kodi playlist
- Remove obsolete check if path is indeed in unicode
- Add unicode representation to Playlist() class
- Separate function to wipe all synched Plex playlists
- Less logging when comparing PKC versions
version 2.9.1:
- Fix On Deck and Recently Added Episodes for shows not appending showname and season and episode number
version 2.9.0:
WARNING: You might have to manually select your PKC widgets again
- versions 2.8.8 - 2.8.11 for everyone
- Fix AttributeError: 'NoneType' object has no attribute 'attrib' on playback startup
- Add new Lithuanian translations (thanks @egidusm)
version 2.8.11 (beta only):
- Support for the Up Next Kodi add-on
- Fix casting to PlexKodiConnect always starting the first episode
- Rename video nodes for ondeck
version 2.8.10 (beta only):
- Fix broken PKC update
version 2.8.9 (beta only):
- Fix sections that are not synced not displaying menu but entire library
- Provide more metadata for unsynced directory-like items like a tv show
- Fix 'Plex.nodes."id".path' not linking directly to entire library
version 2.8.8 (beta only):
WARNING: You might have to manually select your PKC widgets again
- Ensure correct Kodi Container.Type is set for PKC widgets
- Fix missing cast artwork if an actor also acted as director or writer for another movie. You will have to manually reset the Kodi DB.
version 2.8.7:
- Fix PKC potentially marking a video as watched on startup; don't sync time by toggling a video watch status but use PMS epoch time
version 2.8.6:
- Fix PKC creating thousands of playlists if a single Kodi playlist wasn't unique
- Fix FutureWarning
version 2.8.5:
- Fix Trakt add-on not recognizing id of tv shows (you will need to manually reset the Kodi database in the PKC settings under Advanced)
- Update translations
version 2.8.4:
- Fix for Kodi 17 Krypton TypeError on playback start: 'offscreen' is an invalid keyword argument for this function
- Fix widgets not being populated after very first PlexKodiConnect library sync without a restart of Kodi
- Don't restart Kodi if user chose to enter PKC settings on install
version 2.8.3:
- Versions 2.8.1-2.8.2 for everyone
version 2.8.2 (beta only):
- Add an additional, faster On Deck node for movies (for tv shows, this is impossible, unfortunately)
- Introduce limits to the number of videos shown in PKC widgets to speed them up
- Fix TypeError for Direct Paths: init() got an unexpected keyword argument item
- Fix In Progress widgets being broken and tv shows showing up as completely watched
- Update translations
version 2.8.1 (beta only):
- Fix playback startup and RuntimeError: Unknown exception thrown from the call "XBMCAddon::xbmcplugin::setResolvedUrl"
- Refactor Plex API
- Fix TV Show clearlogo not displaying during playback
- Fix rare UnicodeDecodeError on library sync
- Add additional info dialog for PKC synching playlists
- Update translations
version 2.8.0:
- Finally fix Kodi crashing on playback startup for add-on paths!
- All the good stuff from 2.7.15-2.7.18 for everyone
version 2.7.18 (beta only):
- Fix Kodi always playing the same file version of a video if several are present
- Also play trailers if user chose to resume movie from the beginning
- Ask user whether to resume if using Direct Paths and user initiated playback via PMS
- Fix video thrown by Plex Companion not resuming
version 2.7.17 (beta only):
- Another attempt to keep Kodi from crashing on playback startup
version 2.7.16 (beta only):
- Hopefully fix Kodi crashing on playback startup for good
version 2.7.15 (beta only):
- Hopefully fix Kodi crashing on playback startup
- Refresh widgets only on homescreen to prevent cursor from jumping within libraries
- Don't refresh container when user chose to delete or refresh an item from the context menu
version 2.7.14:
- Correctly clear window variables e.g. on user switch
- Reload skin on resetting PKC video nodes
- Fix last-played node value to ensure a playcount greater than zero
- 2.7.11-2.7.13 for everyone
version 2.7.13 (beta only):
- Fix transcoding not working
- Fix 4k H265 not being transcoded
- Fix some appearance tweak settings
- Fix music and picture nodes pointing to video library
- Fix unequality when comparing sections
- Fix Plex Companion logging error messages
version 2.7.12 (beta only):
- Fix UnicodeEncodeError on playback startup for direct paths
- Attempt to fix rare Kodi crash on PKC exit
version 2.7.11 (beta only):
- Fixes to unicode
- Cleanup code, remove some obsolet methods and functions
- Fix FutureWarning
version 2.7.10:
- Fix duplicate music entries for direct paths (you will need to manually reset the Kodi database)
- Fix UnicodeEncodeError for Direct Paths and some PKC video nodes
- Fix playback sometimes not being reported for direct paths
- Update translations
version 2.7.9:
- Wait for PKC to authorize before loading widgets
- Fix UnicodeDecodeError for libraries with non-ASCII paths
- Fix TypeError on Kodi start
- Fix Kodi Masterlock for nfs paths (requires restart)
version 2.7.8:
- Fix widgets not working in some cases like NVidia Shield
- Fix appending of show title, season and episode number
- Fix node paths for skins
version 2.7.7:
- Fix sync not working due to non-ASCII Plex library names
- Fix PKC synching playstate to wrong user on profile switch. Be aware that Kodi profile switches are error-prone
- Fix playback sometimes not being reported for direct paths
- Fix float() argument must be a string or a number
- Fix nodes for skin use
- Fix 'NoneType' object has no attribute 'kodi_path'
version 2.7.6:
- Make 2.7.5 available for everyone
version 2.7.5:
- Giant overhaul of widgets
- Fix some KeyErrors when playing songs
- Fix rare cases where playlists were being created
version 2.7.4:
- Fix PKC not synching new items if an older Kodi db is present
version 2.7.3:
- Fix PKC trying to initialize playqueues over and over again
- Fix PKC not starting due to a higher version Kodi database
version 2.7.2:
- Fix Kodi profile switch not working correctly and PKC not exiting cleanly
version 2.7.1:
- Fix playback not starting at all
- Fix rare TypeError: unsupported operand type(s) for /: 'NoneType' and 'int' on playback startup
- Improve plex db lookups by creating better db indicees
- Fix background sync crashing in rare cases
- Update translations
- Add Ko-fi donate button
version 2.7.0:
- WARNING: You will need to reset the Kodi database if you're using the stable version of PKC!
- Version 2.6.6-9 for everyone
- Choose which Plex libraries get synched to Kodi
version 2.6.9 (beta only):
- Fix PKC crashing on resetting the database
version 2.6.8 (beta only):
- Choose which Plex libraries get synched to Kodi
- Fix PKC becoming unresponsive
- Fix rare case where thousands of identical playlists could be generated
- Fix movies or shows disappearing in fringe cases
- Fix processing of collections in special cases
- Implement Codacy suggestions
version 2.6.7 (beta only):
- Fix "Unauthorized for PMS" e.g. on switching Plex users
- Improve error messages when playback failes
version 2.6.6 (beta only):
- WARNING: You will need to reset the Kodi database!
- Greatly speed up sync for episodes, especially for large libraries
- Allow websocket redirects. Never allow insecure HTTPs connections for Kodi Leia
- Optimize headers for communication with PMS to appear like a Plex Media Player
- Fix PMS log entries 'Unable to find client profile for device'
- Improve sync dialog
version 2.6.5:
- Fix extras not playing
- Hide "Verify SSL certificate" setting for Kodi 18 Krypton
- Improve logging
- Update translations
version 2.6.4:
- Fix music items getting deleted on startup
- Never ignore SSL certificate errors for Kodi >= 18 - just like Kodi
- Fix playback not starting at the beginning
- Improve dialog to manually enter PMS IP and port
- Show logged in Plex home user in the settings and allow changing it
- Update German strings
- Implement Codacy suggestions
version 2.6.3:
- Fix PKC crashing on Xbox
version 2.6.2:
- Fix playlist sync: sequence item 0: expected string or unicode
- Fix PKC not deleting all the items it should
- Fix keyError 'sessionKey' for weird PMS messages
- Fix artwork caching AttributeError: 'ImageCachingThread' object has no attribute 'cancel'
- Improve pop-up "Searching for PMS"
- Fix FutureWarning
version 2.6.1:
- WARNING: You will need to reset the Kodi database!
- Fix TV sections not being deleted e.g. after user switch
- Don't show a library sync error pop-up when full sync is interrupted
- Fix to correctly escape paths
- Update translations
version 2.6.0:
- Support for Kodi 18 Leia
- Big overhaul of the synching process, it's now much faster
- PKC now supports really big Plex and Kodi libraries
- Too many other improvements to recount. See the changelog for the 2.5.x versions
Furthermore:
- Don't lock Plex DB when processing websocket messages
- Fix KeyError: u'kodi_fileid' for some Plex websocket messages
- Update translations
version 2.5.23 (beta only):
- Hopefully fix slow playback startup just after Kodi startup
- Better, safer way to enter network credentials for Direct Paths
- Fix check whether a direct path is accessible
- Fix OperationalError: no such table on database reset
- Fix widgets not displaying correct playstate after PKC startup
- Fix 'NoneType' object has no attribute 'execute' when Plex artwork is not synced and an item is deleted
- Update translations
- Log whether Plex artwork is synced to Kodi
version 2.5.22 (beta only):
- Fix rare EOFError and PKC starting wrong video as a consequence
version 2.5.21 (beta only):
- Fix KodiVideoDB object has no attribute kodiconn
- Fix local variable 'set_api' referenced before assignment
version 2.5.20 (beta only):
- Begin a new transaction when database was locked
- Fix browsing to show from info dialog
- Fix rare KeyError if user is playing something somewhere else
version 2.5.19 (beta only):
- Fix crash on startup-sync due to missing albums
- Fix browsing to show from info dialog
version 2.5.18 (beta only):
- Fix playback start: Don't lock databases when starting playback
- Refresh Kodi view only once on full syncs
- Ignore playstate updates for full sync time stamps croneter committed
- Try even longer to write to Kodi database
- Fix some items rarely not being synced
version 2.5.17 (beta only):
- Fix playback not starting for really large libraries
version 2.5.16 (beta only):
- Fix KeyError due to malformed PMS messages
- Fix to database parameter must be string
version 2.5.15 (beta only):
- Make PKC potentially compatible with several database schemas
- Support for Kodi 18 Leia RC 5.2
- Increase number of attempts to write to Kodi DB
- Further increase database sync resiliance
version 2.5.14 (beta only):
Fix rare OperationalError: Locked Database
version 2.5.13 (beta only):
- Fix playback not starting up
- Fix Plex channels and watch later not working
- Hopefully fix playstate not being synced to PMS
version 2.5.12 (beta only):
- WARNING: You will need to reset the Kodi database!
- New option to not use Plex artwork
- Add-on paths: Fix resume if playback not initiated with PKC
- Increase database resiliance with sqlite WAL mode
version 2.5.11 (beta only):
- Direct Paths: Fix AttributeError for widgets
version 2.5.10 (beta only):
- Enable Plex Hub listings to be used for widgets
- Finally fix deleteting of items from PMS not working
- Catch sqlite OperationalError for websocket messages
- Revert "Increase database timeouts"
version 2.5.9 (beta only):
- Compatibility with Kodi 18 RC 4
- New setting to escape paths e.g. for HTTP direct paths
- Ensure path replacement never contains trailing (back)slash
- Leia: fix resetting of videoplayer autoplay next item
- Don't store identical show artwork for seasons
- Close DB connections while caching images
- Increase database timeouts
- Improve logging for seasons
version 2.5.8 (beta only):
- Hopefully fix Kodi crashing on playback start
- Fix video resuming from old resume point
- Fix database is locked
- Faster way to initialize playlists on the Plex side
- Fix PKC recreating playlists too often
- Shutdown playlist sync if necessary
version 2.5.7 (beta only):
- WARNING: You will need to reset the Kodi database!
- Increase timeout for database connections
- Fix music DB not being wiped on database reset
- Improve Plex playQueue resiliance
version 2.5.6 (beta only):
- Fix many items not getting synced
- Fix episodes not being synced to due a missing season
- Fix some very few items not being synced
- Fix ValueError during sync due to missing Plex timestamp
- Fix resume for episodes for add-on paths
- Fix movies not showing up on switching PMS
- Finish full syncs during playbacks, don't start new ones
- Fix AttributeError when a playlist disappeared
- Close sync dialog if video playback starts
- Don't show sync messages while Kodi is playing something
- Only marking full sync as successful if that is indeed the case
- Optimize code
version 2.5.5 (beta only):
- Fix OperationalError and PKC not starting up
version 2.5.4 (beta only):
- Fix a couple of issues related to episodes
- Fix permanent missing library items if PMS failed to send a single response
- Fix OperationalError: enforce Kodi restart with clean DB once
- Fix switching PMS not recognizing when old PMS is selected
- Fix PKC not automatically connecting to changed PMS IP on startup
- Remove message "Full library sync finished"
- Fix PKC not automatically connecting to changed PMS IP on startup
- Remove cProfile program metrics measurements
version 2.5.3 (beta only):
- Fix Plex sections not showing up or disappearing
version 2.5.2 (beta only):
- Rewire library sync
- Optimize sqlite transactions
- Replace annoying sync message with PKC settings info
- Add PKC settings status indication for caching
- Fix KeyError when synching playlists
- Fix ImportError for Plex Companion gdm issues
- Increase database connection cache size
- Force-Reboot Kodi immediately if sqlite PRAGMA WAL causes errors
- Force a full sync on switching Plex username
- Fix wierd behavior upon switching to another PMS
- More bugfixes and code optimizations
version 2.5.1 (beta only):
- Fix OSError on resetting the database
version 2.5.0 (beta only):
- Huge rewrite of the sync mechanism - it should now be faster and more stable
- Sync huge Plex libraries now: the sync will load all data bit by bit
- Rewrote code for the main program loop, reducing the need for separate Python threads
- Rewrote and sped up code to access and change Kodi and Plex databases
- Fixes to Kodi 18 Leia music library
- Tons of other small fixes I can't remember
version 2.4.10 (beta only):
- Use xml.etree.cElementTree whenever possible to avoid memory leaks
version 2.4.9:
- Fix Kodi crashing due to PKC memory leak
version 2.4.8:
- Make 2.4.4-2.4.7 available for everyone
version 2.4.7 (beta only):
- Try to fix PKC for Enigma 2
- Fix Kodi 18 wanting to scan tags for songs all the time (you will need to reset the database in the PKC settings)
- Optimize resetting of Kodi and Plex databases
version 2.4.6 (beta only):
- Fix PKC not starting up on Enigma
- Fix sync issues if video lies in root of file system
- Make sure we retain a dummy first music artist entry
- Increase logging
version 2.4.5 (beta only):
- Fix playback not starting up at all
- Rewire Kodi library refreshs
- Wipe Kodi database on first PKC run to more reliably install PKC
version 2.4.4 (beta only):
- Fix rare case when playback would not start-up
- Increase logging
version 2.4.3:
- Fix Kodi addons throwing jsonrpc errors (database reset needed)
version 2.4.2:
- Make version 2.4.1 available for everyone
version 2.4.1 (beta only):
- Hopefully fix endless playlist sync loops
- Ensure shows are deleted before seasons before episodes
- Fix library sync crash on deleting episode with missing season
- Fix numbering of already existing playlist files
- Optimize logging
version 2.4.0:
- Use pretty Plex dialogs for everyone!
version 2.3.14 (beta only):
- Fix AttributeError on forcing texture caching
- Switch to Plex style dialogs
- Include PKC info in plex.tv dialogs
- Include PKC info in user selection dialog
version 2.3.13 (beta only):
- Pretty Plex dialogs for plex.tv sign-in and user selection
- Fix UnicodeDecodeError for PMS with non ASCII chars on local LAN discovery
- Fix add-on settings not opening on installation
- Greatly speed up deleting of items on the Kodi side
- Safely parse XMLs using defusedxml
- Fix PKC trying to sync audio playlists even when audio sync disabled
- Some code cleanup
version 2.3.12:
- Fix Kodi hanging if media stream selection is aborted
- Fix potential sync crash
- Revert "Fix Kodi crash by committing to DB frequently"
version 2.3.11 (beta only):
- Fix Kodi crash by committing to DB frequently
version 2.3.10:
- Compatibility with Kodi 18 Leia Beta 1
- Update translations
- Make version 2.3.9 available for everyone
version 2.3.9 (beta only):
- Fix playback not resuming (Kodi 18 ignores listitem "StartOffset")
- Fix playerid not being retrieved for Kodi 18
- Prefer local trailers; new setting to list extras instead of playing trailer
version 2.3.8:
- Fix typo
- Make version 2.3.4-2.3.7 available for everyone
version 2.3.7 (beta only):
- Fix library sync crash due to exotic playlist characters
- Force-deactivate playlist sync for Microsoft UWP for Kodi 18
version 2.3.6 (beta only):
- Fix PKC not starting by decoupling watchdog/subprocess modules
version 2.3.5 (beta only):
- Fix PKC not starting by importing playlist module only when sync enabled
version 2.3.4 (beta only):
- Fix playback sometimes not starting and UnicodeEncodeError for logging
version 2.3.3:
- Choose trailer if several are present (DB reset required)
version 2.3.2:
- Fix casting to PKC failing
version 2.3.1:
- Fix library sync crashing due to Plex photo albums
version 2.3.0:
Major stable version bump. Highlights:
- Sync Plex playlists to Kodi and Kodi playlists to Plex!
- Support for Plex collection/set artwork
- Many bug fixes, especially Plex Companion
- Tons of code improvements in the hope that someone else will help with developing PKC
Warning: the 2 helper add-ons for movies and tv shows also received an upgrade from 2.0.4 to 2.0.5. If you want to downgrade PKC, be sure to downgrade these add-ons as well!
version 2.2.18 (beta only):
- Fix PKC tv show node "all"
- Move PKC playlist shortcut
version 2.2.17 (beta only):
- Access Plex Hubs. Listing will be different depending on Kodi section!
- Fix year for songs missing
- Fix Plex extras not playing
- Fix rare library sync crash
version 2.2.16 (beta only):
- Enable Kodi libraries for Plex Music libraries
- New Playlists menu item for video libraries
- Only show Plex libraries in the applicable Kodi media category
- Optimize code
version 2.2.15 (beta only):
- Fix ImportError on first PKC run
version 2.2.14 (beta only):
- Hopefully fix playlist sync loops
version 2.2.13 (beta only):
- Fix library sync crash
- Fix switching to __future__ module
- Fix "Prefer Kodi Artwork" toggle doing the exact opposite
- Fix "Prefer Kodi artwork" setting not being visible
version 2.2.12 (beta only):
- Fix slow sync. Fix endless sync of corrupted PMS elements
- Refactor playlist code
- Fix FutureWarning
version 2.2.11 (beta only):
- Fix OnDeck widget for Direct Paths
- Fix Plex Companion crashing when connected to Plex Web
- Fix Plex Companion crash when connected to Plex Web playing playlist music
- Improve Plex playback report when playing music playlist
- Improve reliability in Kodi song playback
- Catch some errors if user mixes audio and video in Kodi playqueue
version 2.2.10 (beta only):
- Fix playlists getting recreated and deleted in an endless loop
- Add some safety nets for playlist sync
- Fix FutureWarning
- Fix playlist sync settings not disappearing
- Optimize code
version 2.2.9 (beta only):
- Hopefully fix Kodi and Plex playlists getting out of sync
- Fix and optimize startup of playlist sync
- Hide certain playlist settings under certain conditions
- Fix errors in Kodi log
version 2.2.8 (beta only):
- Support for Plex collection artwork (PKC settings toggle under Artwork)
- Fix hard PKC reset not working (OSError: no such file)
- Deduplication
- Catch exception
- Update translations
- Extend Kodi metadata
- Update readme
version 2.2.7 (beta only):
- Allow to only sync specific Plex or Kodi playlists
- Don't show artwork sync progress, reduce setting-writes
- Fix playback sometimes not starting up
- Use __future__ for contextmenu.py
- Fix imports
version 2.2.6 (beta only):
- Fix default settings string, only show in English, hopefully fixes PKC loosing its settings
version 2.2.5 (beta only):
- Fix AttributeError and add_update has crashed
version 2.2.4 (beta only):
- Fix LibrarySync crashing due to Plex Companion messages
version 2.2.3 (beta only):
- Compatibility with Kodi Krypton Alpha 2
- Append tv show and SxxExx to episode playlist entries
version 2.2.2 (beta only):
- Fixes to locking mechanisms which resulted in weird behavior in some cases
- Switch to Python __future__ unicode_literals and absolute paths
- Fix UnboundLocalError for playlists
- Check all Kodi database versions before starting PKC
- Fix KeyError on non-PKC playback startup
- Speed up subtitle download to Kodi
- Update translations
- PEP-8 stuff
version 2.2.1 (beta only):
- Fix library sync crash due to PMS sending string, not unicode
- Fix playback from playlists for add-on paths
- Detect playback from a Kodi playlist for add-on paths - because we need some hacks due to Kodi bugs
- Fix add-on paths playstate and Plex Companion for playlists
- Fix Kodi telling Plex companion false playqueue position
- Don't try to get a Kodi library items for Plex clips
- Update translations
version 2.2.0 (beta only):
- Support for syncing Plex playlists to Kodi and vice-versa! (Kodi mixed music and video playlists cannot be supported as Plex does not support them)
version 2.1.6:
- Fix slow sync. Fix endless sync of corrupted PMS elements
version 2.1.5:
- Fix OnDeck widget for Direct Paths
version 2.1.4:
- Fix PKC settings suddenly getting lost
- Don't show artwork sync progress, reduce setting-writes
version 2.1.3:
- Fix default settings string, only show in English, hopefully fixes PKC loosing its settings
version 2.1.2:
- Compatibility with Kodi Krypton Alpha 2
- Check all Kodi database versions before starting PKC
- Fix KeyError on non-PKC playback startup
- PEP-8 stuff
version 2.1.1:
- Fix Library Sync crash on Android
version 2.1.0:
Finally a new update for the stable version. You will need to reconnect to your PMS and reset the Kodi database once. Highlights of v2 include:
- Support for Plex extras
- Huge improvements to Plex Companion
- Fixes to Alexa voice control
- Kodi 18 Leia Alpha 1 support
- Improvements to playback start-up
- Improvements to the syncing mechanism, which should get rid of a ton of small bugs
- Fixes to widgets and resuming playback
- Use of plex.direct paths instead of local IP addresses to ensure the SSL certificates shown by the PMS are deemed valid
- Fix Kodi screensaver
- Faster PKC startup
- And tons of other stuff...</news>
</extension> </extension>
</addon> </addon>

View file

@ -1,3 +1,27 @@
version 3.2.0:
WARNING: Database reset and full resync required
- version 3.1.1-3.1.4 for everyone
version 3.1.4 (beta only):
- Fix Alexa and RuntimeError: dictionary keys changed during iteration
- Fix AttributeError: module 'shutil' has no attribute 'copy_tree'
version 3.1.3 (beta only):
- Add PKC setting to disable verification whether we can access a media file
- Direct paths: corrections to more closely mirror Kodi's way of saving movie and tv show files to the db
- Make sure that the correct file system encoding is used for playlists
- Fix a rare AttributeError when using playlists
- Fix regression: fix add-on paths always falling back to direct paths
version 3.1.2 (beta only):
- Fix ImportError: cannot import name 'dir_util' from 'distutils' on PKC startup
- Fix UnicodeEncodeError if Plex playlist name contains illegal chars
- Fix PKC not showing up as a casting target in some cases
version 3.1.1 (beta only):
- Direct paths: fix filename showing instead of full video metadata during playback
- Update translations
version 3.1.0: version 3.1.0:
- version 3.0.16 and 3.0.17 for everyone - version 3.0.16 and 3.0.17 for everyone
- Fix resume not working if Kodi player start-up is slow - Fix resume not working if Kodi player start-up is slow

View file

@ -1150,6 +1150,11 @@ msgctxt "#39071"
msgid "Current plex.tv status:" msgid "Current plex.tv status:"
msgstr "Současný stav plex.tv:" msgstr "Současný stav plex.tv:"
# PKC Settings - Connection
msgctxt "#39072"
msgid "Background sync connection:"
msgstr ""
# PKC Settings, category name # PKC Settings, category name
msgctxt "#39073" msgctxt "#39073"
msgid "Appearance Tweaks" msgid "Appearance Tweaks"
@ -1225,6 +1230,31 @@ msgctxt "#39085"
msgid "Reload Kodi node files to apply all the settings below" msgid "Reload Kodi node files to apply all the settings below"
msgstr "Znovu načíst Kodi pro aplikování nastavení níže" msgstr "Znovu načíst Kodi pro aplikování nastavení níže"
# PKC Settings - Connection - Background sync connection status
msgctxt "#39089"
msgid "Alexa connection status:"
msgstr ""
# PKC Settings - Connection - Background sync connection status
msgctxt "#39091"
msgid "Timeout - not connected"
msgstr ""
# PKC Settings - Connection - Background sync connection status
msgctxt "#39092"
msgid "IOError - not connected"
msgstr ""
# PKC Settings - Connection - Background sync connection status
msgctxt "#39093"
msgid "Suspended - not connected"
msgstr ""
# PKC Settings - Connection - Background sync connection status
msgctxt "#39094"
msgid "Managed Plex User - not connected"
msgstr ""
msgctxt "#39200" msgctxt "#39200"
msgid "Log-out Plex Home User " msgid "Log-out Plex Home User "
msgstr "Odhlásit uživatele Plex Home " msgstr "Odhlásit uživatele Plex Home "

View file

@ -1153,6 +1153,11 @@ msgctxt "#39071"
msgid "Current plex.tv status:" msgid "Current plex.tv status:"
msgstr "Nuværende plex.tv status:" msgstr "Nuværende plex.tv status:"
# PKC Settings - Connection
msgctxt "#39072"
msgid "Background sync connection:"
msgstr ""
# PKC Settings, category name # PKC Settings, category name
msgctxt "#39073" msgctxt "#39073"
msgid "Appearance Tweaks" msgid "Appearance Tweaks"
@ -1230,6 +1235,31 @@ msgstr ""
"Reload Kodi node filer for alle indstillinger\n" "Reload Kodi node filer for alle indstillinger\n"
"nedeunder" "nedeunder"
# PKC Settings - Connection - Background sync connection status
msgctxt "#39089"
msgid "Alexa connection status:"
msgstr ""
# PKC Settings - Connection - Background sync connection status
msgctxt "#39091"
msgid "Timeout - not connected"
msgstr ""
# PKC Settings - Connection - Background sync connection status
msgctxt "#39092"
msgid "IOError - not connected"
msgstr ""
# PKC Settings - Connection - Background sync connection status
msgctxt "#39093"
msgid "Suspended - not connected"
msgstr ""
# PKC Settings - Connection - Background sync connection status
msgctxt "#39094"
msgid "Managed Plex User - not connected"
msgstr ""
msgctxt "#39200" msgctxt "#39200"
msgid "Log-out Plex Home User " msgid "Log-out Plex Home User "
msgstr "Log ud Plex hjemme bruger " msgstr "Log ud Plex hjemme bruger "

View file

@ -1171,6 +1171,11 @@ msgctxt "#39071"
msgid "Current plex.tv status:" msgid "Current plex.tv status:"
msgstr "Aktueller plex.tv Status:" msgstr "Aktueller plex.tv Status:"
# PKC Settings - Connection
msgctxt "#39072"
msgid "Background sync connection:"
msgstr "Verbindungsstatus Hintergrund-Synchronisation:"
# PKC Settings, category name # PKC Settings, category name
msgctxt "#39073" msgctxt "#39073"
msgid "Appearance Tweaks" msgid "Appearance Tweaks"
@ -1246,6 +1251,31 @@ msgctxt "#39085"
msgid "Reload Kodi node files to apply all the settings below" msgid "Reload Kodi node files to apply all the settings below"
msgstr "Kodi neu laden um Einstellungen unten zu übernehmen" msgstr "Kodi neu laden um Einstellungen unten zu übernehmen"
# PKC Settings - Connection - Background sync connection status
msgctxt "#39089"
msgid "Alexa connection status:"
msgstr "Alexa Verbindungsstatus:"
# PKC Settings - Connection - Background sync connection status
msgctxt "#39091"
msgid "Timeout - not connected"
msgstr "Timeout - nicht verbunden"
# PKC Settings - Connection - Background sync connection status
msgctxt "#39092"
msgid "IOError - not connected"
msgstr "IOError - nicht verbunden"
# PKC Settings - Connection - Background sync connection status
msgctxt "#39093"
msgid "Suspended - not connected"
msgstr "Angehalten - nicht verbunden"
# PKC Settings - Connection - Background sync connection status
msgctxt "#39094"
msgid "Managed Plex User - not connected"
msgstr "Managed Plex User - nicht verbunden"
msgctxt "#39200" msgctxt "#39200"
msgid "Log-out Plex Home User " msgid "Log-out Plex Home User "
msgstr "Plex Home Benutzer abmelden: " msgstr "Plex Home Benutzer abmelden: "

File diff suppressed because it is too large Load diff

View file

@ -1078,6 +1078,11 @@ msgctxt "#39074"
msgid "TV Shows" msgid "TV Shows"
msgstr "" msgstr ""
# PKC Settings - Sync
msgctxt "#39075"
msgid "Verify access to media files while synching"
msgstr ""
# Pop-up during initial sync # Pop-up during initial sync
msgctxt "#39076" msgctxt "#39076"
msgid "If you use several Plex libraries of one kind, e.g. \"Kids Movies\" and \"Parents Movies\", be sure to check the Wiki: https://goo.gl/JFtQV9" msgid "If you use several Plex libraries of one kind, e.g. \"Kids Movies\" and \"Parents Movies\", be sure to check the Wiki: https://goo.gl/JFtQV9"

View file

@ -1166,6 +1166,11 @@ msgctxt "#39071"
msgid "Current plex.tv status:" msgid "Current plex.tv status:"
msgstr "Estado actual de plex.tv:" msgstr "Estado actual de plex.tv:"
# PKC Settings - Connection
msgctxt "#39072"
msgid "Background sync connection:"
msgstr ""
# PKC Settings, category name # PKC Settings, category name
msgctxt "#39073" msgctxt "#39073"
msgid "Appearance Tweaks" msgid "Appearance Tweaks"
@ -1243,6 +1248,31 @@ msgctxt "#39085"
msgid "Reload Kodi node files to apply all the settings below" msgid "Reload Kodi node files to apply all the settings below"
msgstr "Recargar Kodi para aplicar todos los ajustes." msgstr "Recargar Kodi para aplicar todos los ajustes."
# PKC Settings - Connection - Background sync connection status
msgctxt "#39089"
msgid "Alexa connection status:"
msgstr ""
# PKC Settings - Connection - Background sync connection status
msgctxt "#39091"
msgid "Timeout - not connected"
msgstr ""
# PKC Settings - Connection - Background sync connection status
msgctxt "#39092"
msgid "IOError - not connected"
msgstr ""
# PKC Settings - Connection - Background sync connection status
msgctxt "#39093"
msgid "Suspended - not connected"
msgstr ""
# PKC Settings - Connection - Background sync connection status
msgctxt "#39094"
msgid "Managed Plex User - not connected"
msgstr ""
msgctxt "#39200" msgctxt "#39200"
msgid "Log-out Plex Home User " msgid "Log-out Plex Home User "
msgstr "Terminar sesión del usuario de Plex Home " msgstr "Terminar sesión del usuario de Plex Home "

View file

@ -1168,6 +1168,11 @@ msgctxt "#39071"
msgid "Current plex.tv status:" msgid "Current plex.tv status:"
msgstr "Estado actual de plex.tv:" msgstr "Estado actual de plex.tv:"
# PKC Settings - Connection
msgctxt "#39072"
msgid "Background sync connection:"
msgstr ""
# PKC Settings, category name # PKC Settings, category name
msgctxt "#39073" msgctxt "#39073"
msgid "Appearance Tweaks" msgid "Appearance Tweaks"
@ -1245,6 +1250,31 @@ msgctxt "#39085"
msgid "Reload Kodi node files to apply all the settings below" msgid "Reload Kodi node files to apply all the settings below"
msgstr "Recargar Kodi para aplicar todos los ajustes." msgstr "Recargar Kodi para aplicar todos los ajustes."
# PKC Settings - Connection - Background sync connection status
msgctxt "#39089"
msgid "Alexa connection status:"
msgstr ""
# PKC Settings - Connection - Background sync connection status
msgctxt "#39091"
msgid "Timeout - not connected"
msgstr ""
# PKC Settings - Connection - Background sync connection status
msgctxt "#39092"
msgid "IOError - not connected"
msgstr ""
# PKC Settings - Connection - Background sync connection status
msgctxt "#39093"
msgid "Suspended - not connected"
msgstr ""
# PKC Settings - Connection - Background sync connection status
msgctxt "#39094"
msgid "Managed Plex User - not connected"
msgstr ""
msgctxt "#39200" msgctxt "#39200"
msgid "Log-out Plex Home User " msgid "Log-out Plex Home User "
msgstr "Terminar sesión del usuario de Plex Home " msgstr "Terminar sesión del usuario de Plex Home "

View file

@ -1166,6 +1166,11 @@ msgctxt "#39071"
msgid "Current plex.tv status:" msgid "Current plex.tv status:"
msgstr "Estado actual de plex.tv:" msgstr "Estado actual de plex.tv:"
# PKC Settings - Connection
msgctxt "#39072"
msgid "Background sync connection:"
msgstr ""
# PKC Settings, category name # PKC Settings, category name
msgctxt "#39073" msgctxt "#39073"
msgid "Appearance Tweaks" msgid "Appearance Tweaks"
@ -1243,6 +1248,31 @@ msgctxt "#39085"
msgid "Reload Kodi node files to apply all the settings below" msgid "Reload Kodi node files to apply all the settings below"
msgstr "Recargar Kodi para aplicar todos los ajustes." msgstr "Recargar Kodi para aplicar todos los ajustes."
# PKC Settings - Connection - Background sync connection status
msgctxt "#39089"
msgid "Alexa connection status:"
msgstr ""
# PKC Settings - Connection - Background sync connection status
msgctxt "#39091"
msgid "Timeout - not connected"
msgstr ""
# PKC Settings - Connection - Background sync connection status
msgctxt "#39092"
msgid "IOError - not connected"
msgstr ""
# PKC Settings - Connection - Background sync connection status
msgctxt "#39093"
msgid "Suspended - not connected"
msgstr ""
# PKC Settings - Connection - Background sync connection status
msgctxt "#39094"
msgid "Managed Plex User - not connected"
msgstr ""
msgctxt "#39200" msgctxt "#39200"
msgid "Log-out Plex Home User " msgid "Log-out Plex Home User "
msgstr "Terminar sesión del usuario de Plex Home " msgstr "Terminar sesión del usuario de Plex Home "

View file

@ -1180,6 +1180,11 @@ msgctxt "#39071"
msgid "Current plex.tv status:" msgid "Current plex.tv status:"
msgstr "État actuel de plex.tv: " msgstr "État actuel de plex.tv: "
# PKC Settings - Connection
msgctxt "#39072"
msgid "Background sync connection:"
msgstr ""
# PKC Settings, category name # PKC Settings, category name
msgctxt "#39073" msgctxt "#39073"
msgid "Appearance Tweaks" msgid "Appearance Tweaks"
@ -1257,6 +1262,31 @@ msgid "Reload Kodi node files to apply all the settings below"
msgstr "" msgstr ""
"Recharger les fichiers de Kodi pour appliquer tous les paramètres ci-dessous" "Recharger les fichiers de Kodi pour appliquer tous les paramètres ci-dessous"
# PKC Settings - Connection - Background sync connection status
msgctxt "#39089"
msgid "Alexa connection status:"
msgstr ""
# PKC Settings - Connection - Background sync connection status
msgctxt "#39091"
msgid "Timeout - not connected"
msgstr ""
# PKC Settings - Connection - Background sync connection status
msgctxt "#39092"
msgid "IOError - not connected"
msgstr ""
# PKC Settings - Connection - Background sync connection status
msgctxt "#39093"
msgid "Suspended - not connected"
msgstr ""
# PKC Settings - Connection - Background sync connection status
msgctxt "#39094"
msgid "Managed Plex User - not connected"
msgstr ""
msgctxt "#39200" msgctxt "#39200"
msgid "Log-out Plex Home User " msgid "Log-out Plex Home User "
msgstr "Log-out Plex Home User " msgstr "Log-out Plex Home User "

View file

@ -1184,6 +1184,11 @@ msgctxt "#39071"
msgid "Current plex.tv status:" msgid "Current plex.tv status:"
msgstr "État actuel de plex.tv: " msgstr "État actuel de plex.tv: "
# PKC Settings - Connection
msgctxt "#39072"
msgid "Background sync connection:"
msgstr ""
# PKC Settings, category name # PKC Settings, category name
msgctxt "#39073" msgctxt "#39073"
msgid "Appearance Tweaks" msgid "Appearance Tweaks"
@ -1261,6 +1266,31 @@ msgid "Reload Kodi node files to apply all the settings below"
msgstr "" msgstr ""
"Recharger les fichiers de Kodi pour appliquer tous les paramètres ci-dessous" "Recharger les fichiers de Kodi pour appliquer tous les paramètres ci-dessous"
# PKC Settings - Connection - Background sync connection status
msgctxt "#39089"
msgid "Alexa connection status:"
msgstr ""
# PKC Settings - Connection - Background sync connection status
msgctxt "#39091"
msgid "Timeout - not connected"
msgstr ""
# PKC Settings - Connection - Background sync connection status
msgctxt "#39092"
msgid "IOError - not connected"
msgstr ""
# PKC Settings - Connection - Background sync connection status
msgctxt "#39093"
msgid "Suspended - not connected"
msgstr ""
# PKC Settings - Connection - Background sync connection status
msgctxt "#39094"
msgid "Managed Plex User - not connected"
msgstr ""
msgctxt "#39200" msgctxt "#39200"
msgid "Log-out Plex Home User " msgid "Log-out Plex Home User "
msgstr "Log-out Plex Home User " msgstr "Log-out Plex Home User "

View file

@ -1,7 +1,7 @@
# XBMC Media Center language file # XBMC Media Center language file
# Translators: # Translators:
# Croneter None <croneter@gmail.com>, 2019 # Croneter None <croneter@gmail.com>, 2019
# Savage93 <savageistheking@gmail.com>, 2020 # Savage93 <savageistheking@gmail.com>, 2021
# #
msgid "" msgid ""
msgstr "" msgstr ""
@ -9,7 +9,7 @@ msgstr ""
"Report-Msgid-Bugs-To: croneter@gmail.com\n" "Report-Msgid-Bugs-To: croneter@gmail.com\n"
"POT-Creation-Date: 2017-04-15 13:13+0000\n" "POT-Creation-Date: 2017-04-15 13:13+0000\n"
"PO-Revision-Date: 2017-04-30 08:30+0000\n" "PO-Revision-Date: 2017-04-30 08:30+0000\n"
"Last-Translator: Savage93 <savageistheking@gmail.com>, 2020\n" "Last-Translator: Savage93 <savageistheking@gmail.com>, 2021\n"
"Language-Team: Hungarian (Hungary) (https://www.transifex.com/croneter/teams/73837/hu_HU/)\n" "Language-Team: Hungarian (Hungary) (https://www.transifex.com/croneter/teams/73837/hu_HU/)\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
@ -51,6 +51,10 @@ msgid ""
"random password automatically if you haven't done so already. Please confirm" "random password automatically if you haven't done so already. Please confirm"
" the next dialog that you want to enable the webserver now with Yes." " the next dialog that you want to enable the webserver now with Yes."
msgstr "" msgstr ""
"A művészképek gyorsítótárazásához szükség van a Kodi webszerverének "
"bekapcsolására. A PKC beállított egy erős, véletlenszerű jelszót ehhez, "
"amennyiben ezt korábban nem tette meg. Kérem erősítse meg a következő "
"dialógusablakban, hogy be kívánja kapcsolni a webszervert."
msgctxt "#30005" msgctxt "#30005"
msgid "Username: " msgid "Username: "
@ -616,7 +620,7 @@ msgstr "Szinkronizálni kívánt Plex könyvtárak kiválasztása"
# PKC Settings - Playback # PKC Settings - Playback
msgctxt "#30525" msgctxt "#30525"
msgid "Skip intro" msgid "Skip intro"
msgstr "" msgstr "Bevezető kihagyása"
# PKC Settings - Playback # PKC Settings - Playback
msgctxt "#30527" msgctxt "#30527"
@ -684,6 +688,8 @@ msgstr "Film-szett/kollekció képek letöltése a FanArtTV-ről"
msgctxt "#30541" msgctxt "#30541"
msgid "Transcoding: Auto-pick audio and subtitle stream using Plex defaults" msgid "Transcoding: Auto-pick audio and subtitle stream using Plex defaults"
msgstr "" msgstr ""
"Transzkódolás: hang- és feliratsávok automatikus kiválasztása a Plex "
"alapértelmezések alapján"
# PKC Settings - Playback # PKC Settings - Playback
msgctxt "#30542" msgctxt "#30542"
@ -983,7 +989,7 @@ msgstr ""
# PKC Settings - Customize Paths # PKC Settings - Customize Paths
msgctxt "#39090" msgctxt "#39090"
msgid "Safe characters for http(s), dav(s) and (s)ftp urls" msgid "Safe characters for http(s), dav(s) and (s)ftp urls"
msgstr "" msgstr "Biztonságos karakterek http(s), dav(s) és (s)ftp elérési utakhoz"
# PKC Settings - Customize Paths # PKC Settings - Customize Paths
msgctxt "#39037" msgctxt "#39037"
@ -1168,6 +1174,11 @@ msgctxt "#39071"
msgid "Current plex.tv status:" msgid "Current plex.tv status:"
msgstr "Jelenlegi plex.tv állapot:" msgstr "Jelenlegi plex.tv állapot:"
# PKC Settings - Connection
msgctxt "#39072"
msgid "Background sync connection:"
msgstr ""
# PKC Settings, category name # PKC Settings, category name
msgctxt "#39073" msgctxt "#39073"
msgid "Appearance Tweaks" msgid "Appearance Tweaks"
@ -1246,6 +1257,31 @@ msgid "Reload Kodi node files to apply all the settings below"
msgstr "" msgstr ""
"Kodi csomópont fájlok újratöltése az alábbi beállítások alkalmazásához" "Kodi csomópont fájlok újratöltése az alábbi beállítások alkalmazásához"
# PKC Settings - Connection - Background sync connection status
msgctxt "#39089"
msgid "Alexa connection status:"
msgstr ""
# PKC Settings - Connection - Background sync connection status
msgctxt "#39091"
msgid "Timeout - not connected"
msgstr ""
# PKC Settings - Connection - Background sync connection status
msgctxt "#39092"
msgid "IOError - not connected"
msgstr ""
# PKC Settings - Connection - Background sync connection status
msgctxt "#39093"
msgid "Suspended - not connected"
msgstr ""
# PKC Settings - Connection - Background sync connection status
msgctxt "#39094"
msgid "Managed Plex User - not connected"
msgstr ""
msgctxt "#39200" msgctxt "#39200"
msgid "Log-out Plex Home User " msgid "Log-out Plex Home User "
msgstr "Kijelentkezés az otthoni Plex felhasználó fiókból: " msgstr "Kijelentkezés az otthoni Plex felhasználó fiókból: "

View file

@ -1169,6 +1169,11 @@ msgctxt "#39071"
msgid "Current plex.tv status:" msgid "Current plex.tv status:"
msgstr "Stato attuale di plex.tv:" msgstr "Stato attuale di plex.tv:"
# PKC Settings - Connection
msgctxt "#39072"
msgid "Background sync connection:"
msgstr ""
# PKC Settings, category name # PKC Settings, category name
msgctxt "#39073" msgctxt "#39073"
msgid "Appearance Tweaks" msgid "Appearance Tweaks"
@ -1246,6 +1251,31 @@ msgstr ""
"Ricarica i nodi di file di Kodi per applicare tutte le impostazioni di " "Ricarica i nodi di file di Kodi per applicare tutte le impostazioni di "
"sotto" "sotto"
# PKC Settings - Connection - Background sync connection status
msgctxt "#39089"
msgid "Alexa connection status:"
msgstr ""
# PKC Settings - Connection - Background sync connection status
msgctxt "#39091"
msgid "Timeout - not connected"
msgstr ""
# PKC Settings - Connection - Background sync connection status
msgctxt "#39092"
msgid "IOError - not connected"
msgstr ""
# PKC Settings - Connection - Background sync connection status
msgctxt "#39093"
msgid "Suspended - not connected"
msgstr ""
# PKC Settings - Connection - Background sync connection status
msgctxt "#39094"
msgid "Managed Plex User - not connected"
msgstr ""
msgctxt "#39200" msgctxt "#39200"
msgid "Log-out Plex Home User " msgid "Log-out Plex Home User "
msgstr "Logout utente Plex " msgstr "Logout utente Plex "

File diff suppressed because it is too large Load diff

View file

@ -1162,6 +1162,11 @@ msgctxt "#39071"
msgid "Current plex.tv status:" msgid "Current plex.tv status:"
msgstr "Dabartinė „plex.tv“ būsena:" msgstr "Dabartinė „plex.tv“ būsena:"
# PKC Settings - Connection
msgctxt "#39072"
msgid "Background sync connection:"
msgstr ""
# PKC Settings, category name # PKC Settings, category name
msgctxt "#39073" msgctxt "#39073"
msgid "Appearance Tweaks" msgid "Appearance Tweaks"
@ -1239,6 +1244,31 @@ msgstr ""
"Atnaujinkite „Kodi“ mazgų failus, kad galėtumėte taikyti visus toliau " "Atnaujinkite „Kodi“ mazgų failus, kad galėtumėte taikyti visus toliau "
"pateiktus nustatymus" "pateiktus nustatymus"
# PKC Settings - Connection - Background sync connection status
msgctxt "#39089"
msgid "Alexa connection status:"
msgstr ""
# PKC Settings - Connection - Background sync connection status
msgctxt "#39091"
msgid "Timeout - not connected"
msgstr ""
# PKC Settings - Connection - Background sync connection status
msgctxt "#39092"
msgid "IOError - not connected"
msgstr ""
# PKC Settings - Connection - Background sync connection status
msgctxt "#39093"
msgid "Suspended - not connected"
msgstr ""
# PKC Settings - Connection - Background sync connection status
msgctxt "#39094"
msgid "Managed Plex User - not connected"
msgstr ""
msgctxt "#39200" msgctxt "#39200"
msgid "Log-out Plex Home User " msgid "Log-out Plex Home User "
msgstr "Atjungti „Plex“ namų vartotoją" msgstr "Atjungti „Plex“ namų vartotoją"

View file

@ -1144,6 +1144,11 @@ msgctxt "#39071"
msgid "Current plex.tv status:" msgid "Current plex.tv status:"
msgstr "Pašreizējais plex.tv statuss:" msgstr "Pašreizējais plex.tv statuss:"
# PKC Settings - Connection
msgctxt "#39072"
msgid "Background sync connection:"
msgstr ""
# PKC Settings, category name # PKC Settings, category name
msgctxt "#39073" msgctxt "#39073"
msgid "Appearance Tweaks" msgid "Appearance Tweaks"
@ -1212,6 +1217,31 @@ msgctxt "#39085"
msgid "Reload Kodi node files to apply all the settings below" msgid "Reload Kodi node files to apply all the settings below"
msgstr "" msgstr ""
# PKC Settings - Connection - Background sync connection status
msgctxt "#39089"
msgid "Alexa connection status:"
msgstr ""
# PKC Settings - Connection - Background sync connection status
msgctxt "#39091"
msgid "Timeout - not connected"
msgstr ""
# PKC Settings - Connection - Background sync connection status
msgctxt "#39092"
msgid "IOError - not connected"
msgstr ""
# PKC Settings - Connection - Background sync connection status
msgctxt "#39093"
msgid "Suspended - not connected"
msgstr ""
# PKC Settings - Connection - Background sync connection status
msgctxt "#39094"
msgid "Managed Plex User - not connected"
msgstr ""
msgctxt "#39200" msgctxt "#39200"
msgid "Log-out Plex Home User " msgid "Log-out Plex Home User "
msgstr "" msgstr ""

View file

@ -1156,6 +1156,11 @@ msgctxt "#39071"
msgid "Current plex.tv status:" msgid "Current plex.tv status:"
msgstr "Huidige status van de plex.tv:" msgstr "Huidige status van de plex.tv:"
# PKC Settings - Connection
msgctxt "#39072"
msgid "Background sync connection:"
msgstr ""
# PKC Settings, category name # PKC Settings, category name
msgctxt "#39073" msgctxt "#39073"
msgid "Appearance Tweaks" msgid "Appearance Tweaks"
@ -1234,6 +1239,31 @@ msgstr ""
"Herlaad de Kodi node bestanden om alles onderstaande instellingen door te " "Herlaad de Kodi node bestanden om alles onderstaande instellingen door te "
"voeren" "voeren"
# PKC Settings - Connection - Background sync connection status
msgctxt "#39089"
msgid "Alexa connection status:"
msgstr ""
# PKC Settings - Connection - Background sync connection status
msgctxt "#39091"
msgid "Timeout - not connected"
msgstr ""
# PKC Settings - Connection - Background sync connection status
msgctxt "#39092"
msgid "IOError - not connected"
msgstr ""
# PKC Settings - Connection - Background sync connection status
msgctxt "#39093"
msgid "Suspended - not connected"
msgstr ""
# PKC Settings - Connection - Background sync connection status
msgctxt "#39094"
msgid "Managed Plex User - not connected"
msgstr ""
msgctxt "#39200" msgctxt "#39200"
msgid "Log-out Plex Home User " msgid "Log-out Plex Home User "
msgstr "Log-out Plex Home gebruiker " msgstr "Log-out Plex Home gebruiker "

View file

@ -1149,6 +1149,11 @@ msgctxt "#39071"
msgid "Current plex.tv status:" msgid "Current plex.tv status:"
msgstr "Aktuell plex.tv statys:" msgstr "Aktuell plex.tv statys:"
# PKC Settings - Connection
msgctxt "#39072"
msgid "Background sync connection:"
msgstr ""
# PKC Settings, category name # PKC Settings, category name
msgctxt "#39073" msgctxt "#39073"
msgid "Appearance Tweaks" msgid "Appearance Tweaks"
@ -1224,6 +1229,31 @@ msgctxt "#39085"
msgid "Reload Kodi node files to apply all the settings below" msgid "Reload Kodi node files to apply all the settings below"
msgstr "" msgstr ""
# PKC Settings - Connection - Background sync connection status
msgctxt "#39089"
msgid "Alexa connection status:"
msgstr ""
# PKC Settings - Connection - Background sync connection status
msgctxt "#39091"
msgid "Timeout - not connected"
msgstr ""
# PKC Settings - Connection - Background sync connection status
msgctxt "#39092"
msgid "IOError - not connected"
msgstr ""
# PKC Settings - Connection - Background sync connection status
msgctxt "#39093"
msgid "Suspended - not connected"
msgstr ""
# PKC Settings - Connection - Background sync connection status
msgctxt "#39094"
msgid "Managed Plex User - not connected"
msgstr ""
msgctxt "#39200" msgctxt "#39200"
msgid "Log-out Plex Home User " msgid "Log-out Plex Home User "
msgstr "Logg av Plex Home User" msgstr "Logg av Plex Home User"

File diff suppressed because it is too large Load diff

View file

@ -1145,6 +1145,11 @@ msgctxt "#39071"
msgid "Current plex.tv status:" msgid "Current plex.tv status:"
msgstr "Estado atual da plex.tv:" msgstr "Estado atual da plex.tv:"
# PKC Settings - Connection
msgctxt "#39072"
msgid "Background sync connection:"
msgstr ""
# PKC Settings, category name # PKC Settings, category name
msgctxt "#39073" msgctxt "#39073"
msgid "Appearance Tweaks" msgid "Appearance Tweaks"
@ -1217,6 +1222,31 @@ msgctxt "#39085"
msgid "Reload Kodi node files to apply all the settings below" msgid "Reload Kodi node files to apply all the settings below"
msgstr "" msgstr ""
# PKC Settings - Connection - Background sync connection status
msgctxt "#39089"
msgid "Alexa connection status:"
msgstr ""
# PKC Settings - Connection - Background sync connection status
msgctxt "#39091"
msgid "Timeout - not connected"
msgstr ""
# PKC Settings - Connection - Background sync connection status
msgctxt "#39092"
msgid "IOError - not connected"
msgstr ""
# PKC Settings - Connection - Background sync connection status
msgctxt "#39093"
msgid "Suspended - not connected"
msgstr ""
# PKC Settings - Connection - Background sync connection status
msgctxt "#39094"
msgid "Managed Plex User - not connected"
msgstr ""
msgctxt "#39200" msgctxt "#39200"
msgid "Log-out Plex Home User " msgid "Log-out Plex Home User "
msgstr "Sair da sessão do Utilizador Caseiro Plex" msgstr "Sair da sessão do Utilizador Caseiro Plex"

View file

@ -1148,6 +1148,11 @@ msgctxt "#39071"
msgid "Current plex.tv status:" msgid "Current plex.tv status:"
msgstr "Estado atual da plex.tv:" msgstr "Estado atual da plex.tv:"
# PKC Settings - Connection
msgctxt "#39072"
msgid "Background sync connection:"
msgstr ""
# PKC Settings, category name # PKC Settings, category name
msgctxt "#39073" msgctxt "#39073"
msgid "Appearance Tweaks" msgid "Appearance Tweaks"
@ -1220,6 +1225,31 @@ msgctxt "#39085"
msgid "Reload Kodi node files to apply all the settings below" msgid "Reload Kodi node files to apply all the settings below"
msgstr "" msgstr ""
# PKC Settings - Connection - Background sync connection status
msgctxt "#39089"
msgid "Alexa connection status:"
msgstr ""
# PKC Settings - Connection - Background sync connection status
msgctxt "#39091"
msgid "Timeout - not connected"
msgstr ""
# PKC Settings - Connection - Background sync connection status
msgctxt "#39092"
msgid "IOError - not connected"
msgstr ""
# PKC Settings - Connection - Background sync connection status
msgctxt "#39093"
msgid "Suspended - not connected"
msgstr ""
# PKC Settings - Connection - Background sync connection status
msgctxt "#39094"
msgid "Managed Plex User - not connected"
msgstr ""
msgctxt "#39200" msgctxt "#39200"
msgid "Log-out Plex Home User " msgid "Log-out Plex Home User "
msgstr "Sair da sessão do Utilizador Caseiro Plex" msgstr "Sair da sessão do Utilizador Caseiro Plex"

View file

@ -1161,6 +1161,11 @@ msgctxt "#39071"
msgid "Current plex.tv status:" msgid "Current plex.tv status:"
msgstr "Текущий статус на plex.tv:" msgstr "Текущий статус на plex.tv:"
# PKC Settings - Connection
msgctxt "#39072"
msgid "Background sync connection:"
msgstr ""
# PKC Settings, category name # PKC Settings, category name
msgctxt "#39073" msgctxt "#39073"
msgid "Appearance Tweaks" msgid "Appearance Tweaks"
@ -1236,6 +1241,31 @@ msgctxt "#39085"
msgid "Reload Kodi node files to apply all the settings below" msgid "Reload Kodi node files to apply all the settings below"
msgstr "Перезаписать узлы БД Kodi, чтобы применить следующие настройки" msgstr "Перезаписать узлы БД Kodi, чтобы применить следующие настройки"
# PKC Settings - Connection - Background sync connection status
msgctxt "#39089"
msgid "Alexa connection status:"
msgstr ""
# PKC Settings - Connection - Background sync connection status
msgctxt "#39091"
msgid "Timeout - not connected"
msgstr ""
# PKC Settings - Connection - Background sync connection status
msgctxt "#39092"
msgid "IOError - not connected"
msgstr ""
# PKC Settings - Connection - Background sync connection status
msgctxt "#39093"
msgid "Suspended - not connected"
msgstr ""
# PKC Settings - Connection - Background sync connection status
msgctxt "#39094"
msgid "Managed Plex User - not connected"
msgstr ""
msgctxt "#39200" msgctxt "#39200"
msgid "Log-out Plex Home User " msgid "Log-out Plex Home User "
msgstr "Выйти из Plex" msgstr "Выйти из Plex"

View file

@ -1156,6 +1156,11 @@ msgctxt "#39071"
msgid "Current plex.tv status:" msgid "Current plex.tv status:"
msgstr "Nuvarande plex.tv status:" msgstr "Nuvarande plex.tv status:"
# PKC Settings - Connection
msgctxt "#39072"
msgid "Background sync connection:"
msgstr ""
# PKC Settings, category name # PKC Settings, category name
msgctxt "#39073" msgctxt "#39073"
msgid "Appearance Tweaks" msgid "Appearance Tweaks"
@ -1230,6 +1235,31 @@ msgctxt "#39085"
msgid "Reload Kodi node files to apply all the settings below" msgid "Reload Kodi node files to apply all the settings below"
msgstr "Ladda om Kodi-nodfiler för att applicera alla inställningar nedan" msgstr "Ladda om Kodi-nodfiler för att applicera alla inställningar nedan"
# PKC Settings - Connection - Background sync connection status
msgctxt "#39089"
msgid "Alexa connection status:"
msgstr ""
# PKC Settings - Connection - Background sync connection status
msgctxt "#39091"
msgid "Timeout - not connected"
msgstr ""
# PKC Settings - Connection - Background sync connection status
msgctxt "#39092"
msgid "IOError - not connected"
msgstr ""
# PKC Settings - Connection - Background sync connection status
msgctxt "#39093"
msgid "Suspended - not connected"
msgstr ""
# PKC Settings - Connection - Background sync connection status
msgctxt "#39094"
msgid "Managed Plex User - not connected"
msgstr ""
msgctxt "#39200" msgctxt "#39200"
msgid "Log-out Plex Home User " msgid "Log-out Plex Home User "
msgstr "Logga ut Plex Home-användare" msgstr "Logga ut Plex Home-användare"

View file

@ -1158,6 +1158,11 @@ msgctxt "#39071"
msgid "Current plex.tv status:" msgid "Current plex.tv status:"
msgstr "Поточний plex.tv статус:" msgstr "Поточний plex.tv статус:"
# PKC Settings - Connection
msgctxt "#39072"
msgid "Background sync connection:"
msgstr ""
# PKC Settings, category name # PKC Settings, category name
msgctxt "#39073" msgctxt "#39073"
msgid "Appearance Tweaks" msgid "Appearance Tweaks"
@ -1235,6 +1240,31 @@ msgid "Reload Kodi node files to apply all the settings below"
msgstr "" msgstr ""
"Перезавантажити файли вузла Kodi для застосування всіх наступних налаштувань" "Перезавантажити файли вузла Kodi для застосування всіх наступних налаштувань"
# PKC Settings - Connection - Background sync connection status
msgctxt "#39089"
msgid "Alexa connection status:"
msgstr ""
# PKC Settings - Connection - Background sync connection status
msgctxt "#39091"
msgid "Timeout - not connected"
msgstr ""
# PKC Settings - Connection - Background sync connection status
msgctxt "#39092"
msgid "IOError - not connected"
msgstr ""
# PKC Settings - Connection - Background sync connection status
msgctxt "#39093"
msgid "Suspended - not connected"
msgstr ""
# PKC Settings - Connection - Background sync connection status
msgctxt "#39094"
msgid "Managed Plex User - not connected"
msgstr ""
msgctxt "#39200" msgctxt "#39200"
msgid "Log-out Plex Home User " msgid "Log-out Plex Home User "
msgstr "Вийти з профілю користувача Plex Home" msgstr "Вийти з профілю користувача Plex Home"

View file

@ -1114,6 +1114,11 @@ msgctxt "#39071"
msgid "Current plex.tv status:" msgid "Current plex.tv status:"
msgstr "当前plex.tv状态" msgstr "当前plex.tv状态"
# PKC Settings - Connection
msgctxt "#39072"
msgid "Background sync connection:"
msgstr ""
# PKC Settings, category name # PKC Settings, category name
msgctxt "#39073" msgctxt "#39073"
msgid "Appearance Tweaks" msgid "Appearance Tweaks"
@ -1182,6 +1187,31 @@ msgctxt "#39085"
msgid "Reload Kodi node files to apply all the settings below" msgid "Reload Kodi node files to apply all the settings below"
msgstr "" msgstr ""
# PKC Settings - Connection - Background sync connection status
msgctxt "#39089"
msgid "Alexa connection status:"
msgstr ""
# PKC Settings - Connection - Background sync connection status
msgctxt "#39091"
msgid "Timeout - not connected"
msgstr ""
# PKC Settings - Connection - Background sync connection status
msgctxt "#39092"
msgid "IOError - not connected"
msgstr ""
# PKC Settings - Connection - Background sync connection status
msgctxt "#39093"
msgid "Suspended - not connected"
msgstr ""
# PKC Settings - Connection - Background sync connection status
msgctxt "#39094"
msgid "Managed Plex User - not connected"
msgstr ""
msgctxt "#39200" msgctxt "#39200"
msgid "Log-out Plex Home User " msgid "Log-out Plex Home User "
msgstr "退出Plex家庭用户 " msgstr "退出Plex家庭用户 "

View file

@ -1110,6 +1110,11 @@ msgctxt "#39071"
msgid "Current plex.tv status:" msgid "Current plex.tv status:"
msgstr "plex.tv 狀態︰" msgstr "plex.tv 狀態︰"
# PKC Settings - Connection
msgctxt "#39072"
msgid "Background sync connection:"
msgstr ""
# PKC Settings, category name # PKC Settings, category name
msgctxt "#39073" msgctxt "#39073"
msgid "Appearance Tweaks" msgid "Appearance Tweaks"
@ -1178,6 +1183,31 @@ msgctxt "#39085"
msgid "Reload Kodi node files to apply all the settings below" msgid "Reload Kodi node files to apply all the settings below"
msgstr "" msgstr ""
# PKC Settings - Connection - Background sync connection status
msgctxt "#39089"
msgid "Alexa connection status:"
msgstr ""
# PKC Settings - Connection - Background sync connection status
msgctxt "#39091"
msgid "Timeout - not connected"
msgstr ""
# PKC Settings - Connection - Background sync connection status
msgctxt "#39092"
msgid "IOError - not connected"
msgstr ""
# PKC Settings - Connection - Background sync connection status
msgctxt "#39093"
msgid "Suspended - not connected"
msgstr ""
# PKC Settings - Connection - Background sync connection status
msgctxt "#39094"
msgid "Managed Plex User - not connected"
msgstr ""
msgctxt "#39200" msgctxt "#39200"
msgid "Log-out Plex Home User " msgid "Log-out Plex Home User "
msgstr "登出Plex Home用戶 " msgstr "登出Plex Home用戶 "

View file

@ -69,6 +69,8 @@ class Sync(object):
self.run_lib_scan = None self.run_lib_scan = None
# Set if user decided to cancel sync # Set if user decided to cancel sync
self.stop_sync = False self.stop_sync = False
# Do we check whether we can access a media file?
self.check_media_file_existence = False
# Could we access the paths? # Could we access the paths?
self.path_verified = False self.path_verified = False
@ -92,6 +94,8 @@ class Sync(object):
def load(self): def load(self):
self.direct_paths = utils.settings('useDirectPaths') == '1' self.direct_paths = utils.settings('useDirectPaths') == '1'
self.check_media_file_existence = \
utils.settings('check_media_file_existence') == '1'
self.enable_music = utils.settings('enableMusic') == 'true' self.enable_music = utils.settings('enableMusic') == 'true'
self.artwork = utils.settings('usePlexArtwork') == 'true' self.artwork = utils.settings('usePlexArtwork') == 'true'
self.replace_smb_path = utils.settings('replaceSMB') == 'true' self.replace_smb_path = utils.settings('replaceSMB') == 'true'

View file

@ -48,7 +48,7 @@ def convert_alexa_to_companion(dictionary):
""" """
The params passed by Alexa must first be converted to Companion talk The params passed by Alexa must first be converted to Companion talk
""" """
for key in dictionary: for key in list(dictionary):
if key in v.ALEXA_TO_COMPANION: if key in v.ALEXA_TO_COMPANION:
dictionary[v.ALEXA_TO_COMPANION[key]] = dictionary[key] dictionary[v.ALEXA_TO_COMPANION[key]] = dictionary[key]
del dictionary[key] del dictionary[key]

View file

@ -1,13 +1,26 @@
#!/usr/bin/env python #!/usr/bin/env python
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from logging import getLogger from logging import getLogger
import re
import os
import string
from .common import ItemBase from .common import ItemBase
from ..plex_api import API from ..plex_api import API
from .. import app, variables as v, plex_functions as PF from .. import app, variables as v, plex_functions as PF
from ..path_ops import append_os_sep
LOG = getLogger('PLEX.movies') LOG = getLogger('PLEX.movies')
# Tolerance in years if comparing videos as equal
VIDEOYEAR_TOLERANCE = 1
PUNCTUATION_TRANSLATION = {ord(char): None for char in string.punctuation}
# Punctuation removed in original strings!!
# Matches '2010 The Year We Make Contact 1984'
# from '2010 The Year We Make Contact 1984 720p webrip'
REGEX_MOVIENAME_AND_YEAR = re.compile(
r'''(.+)((?:19|20)\d{2}).*(?!((19|20)\d{2}))''')
class Movie(ItemBase): class Movie(ItemBase):
""" """
@ -37,11 +50,39 @@ class Movie(ItemBase):
fullpath, path, filename = api.fullpath() fullpath, path, filename = api.fullpath()
if app.SYNC.direct_paths and not fullpath.startswith('http'): if app.SYNC.direct_paths and not fullpath.startswith('http'):
kodi_pathid = self.kodidb.add_path(path, if api.subtype:
content='movies', # E.g. homevideos, which have "subtype" flag set
scraper='metadata.local') # Homevideo directories need to be flat by Plex' instructions
library_path, video_path = path, path
else:
# Normal movie libraries
library_path, video_path, filename = split_movie_path(fullpath)
if library_path == video_path:
# "Flat" folder structure where e.g. movies lie all in 1 dir
# E.g.
# 'C:\\Movies\\Pulp Fiction (1994).mkv'
kodi_pathid = self.kodidb.add_path(library_path,
content='movies',
scraper='metadata.local')
path = library_path
kodi_parent_pathid = kodi_pathid
else:
# Plex library contains folders named identical to the
# video file, e.g.
# 'C:\\Movies\\Pulp Fiction (1994)\\Pulp Fiction (1994).mkv'
# Add the "parent" path for the Plex library
kodi_parent_pathid = self.kodidb.add_path(
library_path,
content='movies',
scraper='metadata.local')
# Add this movie's path
kodi_pathid = self.kodidb.add_path(
video_path,
id_parent_path=kodi_parent_pathid)
path = video_path
else: else:
kodi_pathid = self.kodidb.get_path(path) kodi_pathid = self.kodidb.get_path(path)
kodi_parent_pathid = kodi_pathid
if update_item: if update_item:
LOG.info('UPDATE movie plex_id: %s - %s', plex_id, api.title()) LOG.info('UPDATE movie plex_id: %s - %s', plex_id, api.title())
@ -105,8 +146,8 @@ class Movie(ItemBase):
api.list_to_string(api.studios()), api.list_to_string(api.studios()),
api.trailer(), api.trailer(),
api.list_to_string(api.countries()), api.list_to_string(api.countries()),
fullpath, path,
kodi_pathid, kodi_parent_pathid,
api.premiere_date(), api.premiere_date(),
api.userrating()) api.userrating())
@ -243,3 +284,73 @@ class Movie(ItemBase):
return unique_ids.get('imdb', return unique_ids.get('imdb',
unique_ids.get('tmdb', unique_ids.get('tmdb',
unique_ids.get('tvdb'))) unique_ids.get('tvdb')))
def split_movie_path(path):
"""
Implements Plex' video naming convention for movies:
https://support.plex.tv/articles/naming-and-organizing-your-movie-media-files/
Splits a video's path into its librarypath, potential video folder, and
filename.
E.g. path = 'C:\\Movies\\Pulp Fiction (1994)\\Pulp Fiction (1994).mkv'
returns the tuple
('C:\\Movies\\',
'C:\\Movies\\Pulp Fiction (1994)\\',
'Pulp Fiction (1994).mkv')
E.g. path = 'C:\\Movies\\Pulp Fiction (1994).mkv'
returns the tuple
('C:\\Movies\\',
'C:\\Movies\\',
'Pulp Fiction (1994).mkv')
"""
basename, filename = os.path.split(path)
library_path, videofolder = os.path.split(basename)
clean_filename = _clean_name(os.path.splitext(filename)[0])
clean_videofolder = _clean_name(videofolder)
try:
parsed_filename = _parse_videoname_and_year(clean_filename)
except (TypeError, IndexError):
LOG.warn('Could not parse video path, be sure to follow the Plex '
'naming guidelines!! We failed to parse this path: %s', path)
# Be on the safe side and assume that the movie folder structure is
# flat
return append_os_sep(basename), append_os_sep(basename), filename
try:
parsed_videofolder = _parse_videoname_and_year(clean_videofolder)
except (TypeError, IndexError):
# e.g. no year to parse => flat structure
return append_os_sep(basename), append_os_sep(basename), filename
if _parsed_names_alike(parsed_filename, parsed_videofolder):
# e.g.
# filename = The Master.(2012).720p.Blu-ray.axed.mkv
# videofolder = The Master 2012
# or
# filename = National Lampoon's Christmas Vacation (1989)
# [x264-Bluray-1080p DTS-2.0]
# videofolder = Christmas Vacation 1989
return append_os_sep(library_path), append_os_sep(basename), filename
else:
# Flat movie file-stuctrue, all movies in one big directory
return append_os_sep(basename), append_os_sep(basename), filename
def _parsed_names_alike(name1, name2):
return (abs(name2[1] - name1[1]) <= VIDEOYEAR_TOLERANCE and
(name1[0] in name2[0] or name2[0] in name1[0]))
def _clean_name(name):
"""
Returns name with all whitespaces (regex "\\s") and punctuation
(string.punctuation) characters removed; all characters in lowercase
"""
return re.sub('\\s', '', name).translate(PUNCTUATION_TRANSLATION).lower()
def _parse_videoname_and_year(name):
parsed = REGEX_MOVIENAME_AND_YEAR.search(name)
return parsed[1], int(parsed[2])

View file

@ -45,13 +45,15 @@ class KodiVideoDB(common.KodiDBBase):
strContent, strContent,
strScraper, strScraper,
noUpdate, noUpdate,
exclude) exclude,
VALUES (?, ?, ?, ?, ?) allAudio)
VALUES (?, ?, ?, ?, ?, ?)
''' '''
self.cursor.execute(query, (path, self.cursor.execute(query, (path,
kind, kind,
'metadata.local', 'metadata.local',
1, 1,
0,
0)) 0))
@db.catch_operationalerrors @db.catch_operationalerrors
@ -108,11 +110,13 @@ class KodiVideoDB(common.KodiDBBase):
idParentPath, idParentPath,
strContent, strContent,
strScraper, strScraper,
noUpdate) noUpdate,
VALUES (?, ?, ?, ?, ?, ?) exclude,
allAudio)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
''', ''',
(path, date_added, id_parent_path, content, (path, date_added, id_parent_path, content,
scraper, 1)) scraper, 1, 0, 0))
pathid = self.cursor.lastrowid pathid = self.cursor.lastrowid
return pathid return pathid

View file

@ -20,10 +20,10 @@ SHOULD_CANCEL = None
LIBRARY_PATH = path_ops.translate_path('special://profile/library/video/') LIBRARY_PATH = path_ops.translate_path('special://profile/library/video/')
# The video library might not yet exist for this user - create it # The video library might not yet exist for this user - create it
if not path_ops.exists(LIBRARY_PATH): if not path_ops.exists(LIBRARY_PATH):
path_ops.copy_tree( path_ops.copytree(
src=path_ops.translate_path('special://xbmc/system/library/video'), src=path_ops.translate_path('special://xbmc/system/library/video'),
dst=LIBRARY_PATH, dst=LIBRARY_PATH,
preserve_mode=0) # dont copy permission bits so we have write access! copy_function=path_ops.shutil.copyfile)
PLAYLISTS_PATH = path_ops.translate_path("special://profile/playlists/video/") PLAYLISTS_PATH = path_ops.translate_path("special://profile/playlists/video/")
if not path_ops.exists(PLAYLISTS_PATH): if not path_ops.exists(PLAYLISTS_PATH):
path_ops.makedirs(PLAYLISTS_PATH) path_ops.makedirs(PLAYLISTS_PATH)

View file

@ -17,7 +17,6 @@ as well as sources.xml
import shutil import shutil
import os import os
from os import path # allows to use path_ops.path.join, for example from os import path # allows to use path_ops.path.join, for example
from distutils import dir_util
import re import re
import xbmcvfs import xbmcvfs
@ -27,6 +26,17 @@ KODI_ENCODING = 'utf-8'
REGEX_FILE_NUMBERING = re.compile(r'''_(\d\d)\.\w+$''') REGEX_FILE_NUMBERING = re.compile(r'''_(\d\d)\.\w+$''')
def append_os_sep(path):
"""
Appends either a '\\' or '/' - IRRELEVANT of the host OS!! (os.path.join is
dependant on the host OS)
"""
if '/' in path:
return path + '/'
else:
return path + '\\'
def translate_path(path): def translate_path(path):
""" """
Returns the XBMC translated path [unicode] Returns the XBMC translated path [unicode]
@ -155,28 +165,47 @@ def walk(top, topdown=True, onerror=None, followlinks=False):
[x for x in nondirs]) [x for x in nondirs])
def copy_tree(src, dst, *args, **kwargs): def copytree(src, dst, *args, **kwargs):
""" """
Copy an entire directory tree 'src' to a new location 'dst'. Recursively copy an entire directory tree rooted at src to a directory named
dst and return the destination directory. dirs_exist_ok dictates whether to
raise an exception in case dst or any missing parent directory already
exists.
Both 'src' and 'dst' must be directory names. If 'src' is not a Permissions and times of directories are copied with copystat(), individual
directory, raise DistutilsFileError. If 'dst' does not exist, it is files are copied using copy2().
created with 'mkpath()'. The end result of the copy is that every
file in 'src' is copied to 'dst', and directories under 'src' are
recursively copied to 'dst'. Return the list of files that were
copied or might have been copied, using their output name. The
return value is unaffected by 'update' or 'dry_run': it is simply
the list of all files under 'src', with the names changed to be
under 'dst'.
'preserve_mode' and 'preserve_times' are the same as for If symlinks is true, symbolic links in the source tree are represented as
'copy_file'; note that they only apply to regular files, not to symbolic links in the new tree and the metadata of the original links will
directories. If 'preserve_symlinks' is true, symlinks will be be copied as far as the platform allows; if false or omitted, the contents
copied as symlinks (on platforms that support them!); otherwise and metadata of the linked files are copied to the new tree.
(the default), the destination of the symlink will be copied.
'update' and 'verbose' are the same as for 'copy_file'. When symlinks is false, if the file pointed by the symlink doesnt exist, an
exception will be added in the list of errors raised in an Error exception
at the end of the copy process. You can set the optional
ignore_dangling_symlinks flag to true if you want to silence this exception.
Notice that this option has no effect on platforms that dont support
os.symlink().
If ignore is given, it must be a callable that will receive as its arguments
the directory being visited by copytree(), and a list of its contents, as
returned by os.listdir(). Since copytree() is called recursively, the ignore
callable will be called once for each directory that is copied. The callable
must return a sequence of directory and file names relative to the current
directory (i.e. a subset of the items in its second argument); these names
will then be ignored in the copy process. ignore_patterns() can be used to
create such a callable that ignores names based on glob-style patterns.
If exception(s) occur, an Error is raised with a list of reasons.
If copy_function is given, it must be a callable that will be used to copy
each file. It will be called with the source path and the destination path
as arguments. By default, copy2() is used, but any function that supports
the same signature (like copy()) can be used.
Raises an auditing event shutil.copytree with arguments src, dst.
""" """
return dir_util.copy_tree(src, dst, *args, **kwargs) return shutil.copytree(src, dst, *args, **kwargs)
def basename(path): def basename(path):

View file

@ -0,0 +1,35 @@
"""
.. codeauthor:: Tsuyoshi Hombashi <tsuyoshi.hombashi@gmail.com>
"""
from .__version__ import __author__, __copyright__, __email__, __license__, __version__
from ._common import (
Platform,
ascii_symbols,
normalize_platform,
replace_unprintable_char,
unprintable_ascii_chars,
validate_null_string,
validate_pathtype,
)
from ._filename import FileNameSanitizer, is_valid_filename, sanitize_filename, validate_filename
from ._filepath import (
FilePathSanitizer,
is_valid_filepath,
sanitize_file_path,
sanitize_filepath,
validate_file_path,
validate_filepath,
)
from ._ltsv import sanitize_ltsv_label, validate_ltsv_label
from ._symbol import replace_symbol, validate_symbol
from .error import (
ErrorReason,
InvalidCharError,
InvalidLengthError,
InvalidReservedNameError,
NullNameError,
ReservedNameError,
ValidationError,
ValidReservedNameError,
)

View file

@ -0,0 +1,6 @@
__author__ = "Tsuyoshi Hombashi"
__copyright__ = "Copyright 2016, {}".format(__author__)
__license__ = "MIT License"
__version__ = "2.4.1"
__maintainer__ = __author__
__email__ = "tsuyoshi.hombashi@gmail.com"

View file

@ -0,0 +1,137 @@
"""
.. codeauthor:: Tsuyoshi Hombashi <tsuyoshi.hombashi@gmail.com>
"""
import abc
import os
from typing import Optional, Tuple, cast
from ._common import PathType, Platform, PlatformType, normalize_platform, unprintable_ascii_chars
from .error import ReservedNameError, ValidationError
class BaseFile:
_INVALID_PATH_CHARS = "".join(unprintable_ascii_chars)
_INVALID_FILENAME_CHARS = _INVALID_PATH_CHARS + "/"
_INVALID_WIN_PATH_CHARS = _INVALID_PATH_CHARS + ':*?"<>|\t\n\r\x0b\x0c'
_INVALID_WIN_FILENAME_CHARS = _INVALID_FILENAME_CHARS + _INVALID_WIN_PATH_CHARS + "\\"
_ERROR_MSG_TEMPLATE = "invalid char found: invalids=({invalid}), value={value}"
@property
def platform(self) -> Platform:
return self.__platform
@property
def reserved_keywords(self) -> Tuple[str, ...]:
return tuple()
@property
def min_len(self) -> int:
return self._min_len
@property
def max_len(self) -> int:
return self._max_len
def __init__(
self,
min_len: Optional[int],
max_len: Optional[int],
check_reserved: bool,
platform_max_len: Optional[int] = None,
platform: PlatformType = None,
) -> None:
self.__platform = normalize_platform(platform)
self._check_reserved = check_reserved
if min_len is None:
min_len = 1
self._min_len = max(min_len, 1)
if platform_max_len is None:
platform_max_len = self._get_default_max_path_len()
if max_len in [None, -1]:
self._max_len = platform_max_len
else:
self._max_len = cast(int, max_len)
self._max_len = min(self._max_len, platform_max_len)
self._validate_max_len()
def _is_posix(self) -> bool:
return self.platform == Platform.POSIX
def _is_universal(self) -> bool:
return self.platform == Platform.UNIVERSAL
def _is_linux(self) -> bool:
return self.platform == Platform.LINUX
def _is_windows(self) -> bool:
return self.platform == Platform.WINDOWS
def _is_macos(self) -> bool:
return self.platform == Platform.MACOS
def _validate_max_len(self) -> None:
if self.max_len < 1:
raise ValueError("max_len must be greater or equals to one")
if self.min_len > self.max_len:
raise ValueError("min_len must be lower than max_len")
def _get_default_max_path_len(self) -> int:
if self._is_linux():
return 4096
if self._is_windows():
return 260
if self._is_posix() or self._is_macos():
return 1024
return 260 # universal
class AbstractValidator(BaseFile, metaclass=abc.ABCMeta):
@abc.abstractmethod
def validate(self, value: PathType) -> None: # pragma: no cover
pass
def is_valid(self, value: PathType) -> bool:
try:
self.validate(value)
except (TypeError, ValidationError):
return False
return True
def _is_reserved_keyword(self, value: str) -> bool:
return value in self.reserved_keywords
class AbstractSanitizer(BaseFile, metaclass=abc.ABCMeta):
@abc.abstractmethod
def sanitize(self, value: PathType, replacement_text: str = "") -> PathType: # pragma: no cover
pass
class BaseValidator(AbstractValidator):
def _validate_reserved_keywords(self, name: str) -> None:
if not self._check_reserved:
return
root_name = self.__extract_root_name(name)
if self._is_reserved_keyword(root_name.upper()):
raise ReservedNameError(
"'{}' is a reserved name".format(root_name),
reusable_name=False,
reserved_name=root_name,
platform=self.platform,
)
@staticmethod
def __extract_root_name(path: str) -> str:
return os.path.splitext(os.path.basename(path))[0]

View file

@ -0,0 +1,147 @@
"""
.. codeauthor:: Tsuyoshi Hombashi <tsuyoshi.hombashi@gmail.com>
"""
import enum
import platform
import re
import string
from pathlib import Path
from typing import Any, List, Optional, Union, cast
_re_whitespaces = re.compile(r"^[\s]+$")
@enum.unique
class Platform(enum.Enum):
POSIX = "POSIX"
UNIVERSAL = "universal"
LINUX = "Linux"
WINDOWS = "Windows"
MACOS = "macOS"
PathType = Union[str, Path]
PlatformType = Union[str, Platform, None]
def is_pathlike_obj(value: PathType) -> bool:
return isinstance(value, Path)
def validate_pathtype(
text: PathType, allow_whitespaces: bool = False, error_msg: Optional[str] = None
) -> None:
from .error import ErrorReason, ValidationError
if _is_not_null_string(text) or is_pathlike_obj(text):
return
if allow_whitespaces and _re_whitespaces.search(str(text)):
return
if is_null_string(text):
if not error_msg:
error_msg = "the value must be a not empty"
raise ValidationError(
description=error_msg,
reason=ErrorReason.NULL_NAME,
)
raise TypeError("text must be a string: actual={}".format(type(text)))
def validate_null_string(text: PathType, error_msg: Optional[str] = None) -> None:
# Deprecated: alias to validate_pathtype
validate_pathtype(text, False, error_msg)
def preprocess(name: PathType) -> str:
if is_pathlike_obj(name):
name = str(name)
return cast(str, name)
def is_null_string(value: Any) -> bool:
if value is None:
return True
try:
return len(value.strip()) == 0
except AttributeError:
return False
def _is_not_null_string(value: Any) -> bool:
try:
return len(value.strip()) > 0
except AttributeError:
return False
def _get_unprintable_ascii_chars() -> List[str]:
return [chr(c) for c in range(128) if chr(c) not in string.printable]
unprintable_ascii_chars = tuple(_get_unprintable_ascii_chars())
def _get_ascii_symbols() -> List[str]:
symbol_list = [] # type: List[str]
for i in range(128):
c = chr(i)
if c in unprintable_ascii_chars or c in string.digits + string.ascii_letters:
continue
symbol_list.append(c)
return symbol_list
ascii_symbols = tuple(_get_ascii_symbols())
__RE_UNPRINTABLE_CHARS = re.compile(
"[{}]".format(re.escape("".join(unprintable_ascii_chars))), re.UNICODE
)
def replace_unprintable_char(text: str, replacement_text: str = "") -> str:
try:
return __RE_UNPRINTABLE_CHARS.sub(replacement_text, text)
except (TypeError, AttributeError):
raise TypeError("text must be a string")
def normalize_platform(name: PlatformType) -> Platform:
if isinstance(name, Platform):
return name
if name:
name = name.strip().lower()
if name == "posix":
return Platform.POSIX
if name == "auto":
name = platform.system().lower()
if name in ["linux"]:
return Platform.LINUX
if name and name.startswith("win"):
return Platform.WINDOWS
if name in ["mac", "macos", "darwin"]:
return Platform.MACOS
return Platform.UNIVERSAL
def findall_to_str(match: List[Any]) -> str:
return ", ".join([repr(text) for text in match])

View file

@ -0,0 +1,16 @@
_NTFS_RESERVED_FILE_NAMES = (
"$Mft",
"$MftMirr",
"$LogFile",
"$Volume",
"$AttrDef",
"$Bitmap",
"$Boot",
"$BadClus",
"$Secure",
"$Upcase",
"$Extend",
"$Quota",
"$ObjId",
"$Reparse",
) # Only in root directory

View file

@ -0,0 +1,341 @@
"""
.. codeauthor:: Tsuyoshi Hombashi <tsuyoshi.hombashi@gmail.com>
"""
import itertools
import ntpath
import posixpath
import re
from pathlib import Path
from typing import Optional, Pattern, Tuple
from ._base import AbstractSanitizer, BaseFile, BaseValidator
from ._common import (
PathType,
Platform,
PlatformType,
findall_to_str,
is_pathlike_obj,
preprocess,
validate_pathtype,
)
from .error import ErrorReason, InvalidCharError, InvalidLengthError, ValidationError
_DEFAULT_MAX_FILENAME_LEN = 255
_RE_INVALID_FILENAME = re.compile(
"[{:s}]".format(re.escape(BaseFile._INVALID_FILENAME_CHARS)), re.UNICODE
)
_RE_INVALID_WIN_FILENAME = re.compile(
"[{:s}]".format(re.escape(BaseFile._INVALID_WIN_FILENAME_CHARS)), re.UNICODE
)
class FileNameSanitizer(AbstractSanitizer):
def __init__(
self,
min_len: Optional[int] = 1,
max_len: Optional[int] = _DEFAULT_MAX_FILENAME_LEN,
platform: PlatformType = None,
check_reserved: bool = True,
) -> None:
super().__init__(
min_len=min_len,
max_len=max_len,
check_reserved=check_reserved,
platform_max_len=_DEFAULT_MAX_FILENAME_LEN,
platform=platform,
)
self._sanitize_regexp = self._get_sanitize_regexp()
self.__validator = FileNameValidator(
min_len=self.min_len,
max_len=self.max_len,
check_reserved=check_reserved,
platform=self.platform,
)
def sanitize(self, value: PathType, replacement_text: str = "") -> PathType:
try:
validate_pathtype(value, allow_whitespaces=True if not self._is_windows() else False)
except ValidationError as e:
if e.reason == ErrorReason.NULL_NAME:
return ""
raise
sanitized_filename = self._sanitize_regexp.sub(replacement_text, str(value))
sanitized_filename = sanitized_filename[: self.max_len]
try:
self.__validator.validate(sanitized_filename)
except ValidationError as e:
if e.reason == ErrorReason.RESERVED_NAME and e.reusable_name is False:
sanitized_filename = re.sub(
re.escape(e.reserved_name), "{}_".format(e.reserved_name), sanitized_filename
)
elif e.reason == ErrorReason.INVALID_CHARACTER:
if self.platform in [Platform.UNIVERSAL, Platform.WINDOWS]:
sanitized_filename = sanitized_filename.rstrip(" .")
if is_pathlike_obj(value):
return Path(sanitized_filename)
return sanitized_filename
def _get_sanitize_regexp(self) -> Pattern:
if self.platform in [Platform.UNIVERSAL, Platform.WINDOWS]:
return _RE_INVALID_WIN_FILENAME
return _RE_INVALID_FILENAME
class FileNameValidator(BaseValidator):
_WINDOWS_RESERVED_FILE_NAMES = ("CON", "PRN", "AUX", "CLOCK$", "NUL") + tuple(
"{:s}{:d}".format(name, num)
for name, num in itertools.product(("COM", "LPT"), range(1, 10))
)
_MACOS_RESERVED_FILE_NAMES = (":",)
@property
def reserved_keywords(self) -> Tuple[str, ...]:
common_keywords = super().reserved_keywords
if self._is_universal():
return (
common_keywords
+ self._WINDOWS_RESERVED_FILE_NAMES
+ self._MACOS_RESERVED_FILE_NAMES
)
if self._is_windows():
return common_keywords + self._WINDOWS_RESERVED_FILE_NAMES
if self._is_posix() or self._is_macos():
return common_keywords + self._MACOS_RESERVED_FILE_NAMES
return common_keywords
def __init__(
self,
min_len: Optional[int] = 1,
max_len: Optional[int] = _DEFAULT_MAX_FILENAME_LEN,
platform: PlatformType = None,
check_reserved: bool = True,
) -> None:
super().__init__(
min_len=min_len,
max_len=max_len,
check_reserved=check_reserved,
platform_max_len=_DEFAULT_MAX_FILENAME_LEN,
platform=platform,
)
def validate(self, value: PathType) -> None:
validate_pathtype(
value,
allow_whitespaces=False
if self.platform in [Platform.UNIVERSAL, Platform.WINDOWS]
else True,
)
unicode_filename = preprocess(value)
value_len = len(unicode_filename)
self.validate_abspath(unicode_filename)
if value_len > self.max_len:
raise InvalidLengthError(
"filename is too long: expected<={:d}, actual={:d}".format(self.max_len, value_len)
)
if value_len < self.min_len:
raise InvalidLengthError(
"filename is too short: expected>={:d}, actual={:d}".format(self.min_len, value_len)
)
self._validate_reserved_keywords(unicode_filename)
if self._is_universal() or self._is_windows():
self.__validate_win_filename(unicode_filename)
else:
self.__validate_unix_filename(unicode_filename)
def validate_abspath(self, value: str) -> None:
err = ValidationError(
description="found an absolute path ({}), expected a filename".format(value),
platform=self.platform,
reason=ErrorReason.FOUND_ABS_PATH,
)
if self._is_universal() or self._is_windows():
if ntpath.isabs(value):
raise err
if posixpath.isabs(value):
raise err
def __validate_unix_filename(self, unicode_filename: str) -> None:
match = _RE_INVALID_FILENAME.findall(unicode_filename)
if match:
raise InvalidCharError(
self._ERROR_MSG_TEMPLATE.format(
invalid=findall_to_str(match), value=repr(unicode_filename)
)
)
def __validate_win_filename(self, unicode_filename: str) -> None:
match = _RE_INVALID_WIN_FILENAME.findall(unicode_filename)
if match:
raise InvalidCharError(
self._ERROR_MSG_TEMPLATE.format(
invalid=findall_to_str(match), value=repr(unicode_filename)
),
platform=Platform.WINDOWS,
)
if unicode_filename in (".", ".."):
return
if unicode_filename[-1] in (" ", "."):
raise InvalidCharError(
self._ERROR_MSG_TEMPLATE.format(
invalid=re.escape(unicode_filename[-1]), value=repr(unicode_filename)
),
platform=Platform.WINDOWS,
description="Do not end a file or directory name with a space or a period",
)
def validate_filename(
filename: PathType,
platform: Optional[str] = None,
min_len: int = 1,
max_len: int = _DEFAULT_MAX_FILENAME_LEN,
check_reserved: bool = True,
) -> None:
"""Verifying whether the ``filename`` is a valid file name or not.
Args:
filename:
Filename to validate.
platform:
Target platform name of the filename.
.. include:: platform.txt
min_len:
Minimum length of the ``filename``. The value must be greater or equal to one.
Defaults to ``1``.
max_len:
Maximum length of the ``filename``. The value must be lower than:
- ``Linux``: 4096
- ``macOS``: 1024
- ``Windows``: 260
- ``universal``: 260
Defaults to ``255``.
check_reserved:
If |True|, check reserved names of the ``platform``.
Raises:
ValidationError (ErrorReason.INVALID_LENGTH):
If the ``filename`` is longer than ``max_len`` characters.
ValidationError (ErrorReason.INVALID_CHARACTER):
If the ``filename`` includes invalid character(s) for a filename:
|invalid_filename_chars|.
The following characters are also invalid for Windows platform:
|invalid_win_filename_chars|.
ValidationError (ErrorReason.RESERVED_NAME):
If the ``filename`` equals reserved name by OS.
Windows reserved name is as follows:
``"CON"``, ``"PRN"``, ``"AUX"``, ``"NUL"``, ``"COM[1-9]"``, ``"LPT[1-9]"``.
Example:
:ref:`example-validate-filename`
See Also:
`Naming Files, Paths, and Namespaces - Win32 apps | Microsoft Docs
<https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file>`__
"""
FileNameValidator(
platform=platform, min_len=min_len, max_len=max_len, check_reserved=check_reserved
).validate(filename)
def is_valid_filename(
filename: PathType,
platform: Optional[str] = None,
min_len: int = 1,
max_len: Optional[int] = None,
check_reserved: bool = True,
) -> bool:
"""Check whether the ``filename`` is a valid name or not.
Args:
filename:
A filename to be checked.
Example:
:ref:`example-is-valid-filename`
See Also:
:py:func:`.validate_filename()`
"""
return FileNameValidator(
platform=platform, min_len=min_len, max_len=max_len, check_reserved=check_reserved
).is_valid(filename)
def sanitize_filename(
filename: PathType,
replacement_text: str = "",
platform: Optional[str] = None,
max_len: Optional[int] = _DEFAULT_MAX_FILENAME_LEN,
check_reserved: bool = True,
) -> PathType:
"""Make a valid filename from a string.
To make a valid filename the function does:
- Replace invalid characters as file names included in the ``filename``
with the ``replacement_text``. Invalid characters are:
- unprintable characters
- |invalid_filename_chars|
- for Windows (or universal) only: |invalid_win_filename_chars|
- Append underscore (``"_"``) at the tail of the name if sanitized name
is one of the reserved names by operating systems
(only when ``check_reserved`` is |True|).
Args:
filename: Filename to sanitize.
replacement_text:
Replacement text for invalid characters. Defaults to ``""``.
platform:
Target platform name of the filename.
.. include:: platform.txt
max_len:
Maximum length of the ``filename`` length. Truncate the name length if
the ``filename`` length exceeds this value.
Defaults to ``255``.
check_reserved:
If |True|, sanitize reserved names of the ``platform``.
Returns:
Same type as the ``filename`` (str or PathLike object):
Sanitized filename.
Raises:
ValueError:
If the ``filename`` is an invalid filename.
Example:
:ref:`example-sanitize-filename`
"""
return FileNameSanitizer(
platform=platform, max_len=max_len, check_reserved=check_reserved
).sanitize(filename, replacement_text)

View file

@ -0,0 +1,427 @@
"""
.. codeauthor:: Tsuyoshi Hombashi <tsuyoshi.hombashi@gmail.com>
"""
import ntpath
import os.path
import posixpath
import re
from pathlib import Path
from typing import List, Optional, Pattern, Tuple # noqa
from ._base import AbstractSanitizer, BaseFile, BaseValidator
from ._common import (
PathType,
Platform,
PlatformType,
findall_to_str,
is_pathlike_obj,
preprocess,
validate_pathtype,
)
from ._const import _NTFS_RESERVED_FILE_NAMES
from ._filename import FileNameSanitizer, FileNameValidator
from .error import (
ErrorReason,
InvalidCharError,
InvalidLengthError,
ReservedNameError,
ValidationError,
)
_RE_INVALID_PATH = re.compile("[{:s}]".format(re.escape(BaseFile._INVALID_PATH_CHARS)), re.UNICODE)
_RE_INVALID_WIN_PATH = re.compile(
"[{:s}]".format(re.escape(BaseFile._INVALID_WIN_PATH_CHARS)), re.UNICODE
)
class FilePathSanitizer(AbstractSanitizer):
def __init__(
self,
min_len: Optional[int] = 1,
max_len: Optional[int] = None,
platform: PlatformType = None,
check_reserved: bool = True,
normalize: bool = True,
) -> None:
super().__init__(
min_len=min_len,
max_len=max_len,
check_reserved=check_reserved,
platform=platform,
)
self._sanitize_regexp = self._get_sanitize_regexp()
self.__fpath_validator = FilePathValidator(
min_len=self.min_len,
max_len=self.max_len,
check_reserved=check_reserved,
platform=self.platform,
)
self.__fname_sanitizer = FileNameSanitizer(
min_len=self.min_len,
max_len=self.max_len,
check_reserved=check_reserved,
platform=self.platform,
)
self.__normalize = normalize
if self._is_universal() or self._is_windows():
self.__split_drive = ntpath.splitdrive
else:
self.__split_drive = posixpath.splitdrive
def sanitize(self, value: PathType, replacement_text: str = "") -> PathType:
if not value:
return ""
self.__fpath_validator.validate_abspath(value)
unicode_filepath = preprocess(value)
if self.__normalize:
unicode_filepath = os.path.normpath(unicode_filepath)
drive, unicode_filepath = self.__split_drive(unicode_filepath)
sanitized_path = self._sanitize_regexp.sub(replacement_text, unicode_filepath)
if self._is_windows():
path_separator = "\\"
else:
path_separator = "/"
sanitized_entries = [] # type: List[str]
if drive:
sanitized_entries.append(drive)
for entry in sanitized_path.replace("\\", "/").split("/"):
if entry in _NTFS_RESERVED_FILE_NAMES:
sanitized_entries.append("{}_".format(entry))
continue
sanitized_entry = str(self.__fname_sanitizer.sanitize(entry))
if not sanitized_entry:
if not sanitized_entries:
sanitized_entries.append("")
continue
sanitized_entries.append(sanitized_entry)
sanitized_path = path_separator.join(sanitized_entries)
if is_pathlike_obj(value):
return Path(sanitized_path)
return sanitized_path
def _get_sanitize_regexp(self) -> Pattern:
if self.platform in [Platform.UNIVERSAL, Platform.WINDOWS]:
return _RE_INVALID_WIN_PATH
return _RE_INVALID_PATH
class FilePathValidator(BaseValidator):
_RE_NTFS_RESERVED = re.compile(
"|".join("^/{}$".format(re.escape(pattern)) for pattern in _NTFS_RESERVED_FILE_NAMES),
re.IGNORECASE,
)
_MACOS_RESERVED_FILE_PATHS = ("/", ":")
@property
def reserved_keywords(self) -> Tuple[str, ...]:
common_keywords = super().reserved_keywords
if any([self._is_universal(), self._is_posix(), self._is_macos()]):
return common_keywords + self._MACOS_RESERVED_FILE_PATHS
if self._is_linux():
return common_keywords + ("/",)
return common_keywords
def __init__(
self,
min_len: Optional[int] = 1,
max_len: Optional[int] = None,
platform: PlatformType = None,
check_reserved: bool = True,
) -> None:
super().__init__(
min_len=min_len,
max_len=max_len,
check_reserved=check_reserved,
platform=platform,
)
self.__fname_validator = FileNameValidator(
min_len=min_len, max_len=max_len, check_reserved=check_reserved, platform=platform
)
if self._is_universal() or self._is_windows():
self.__split_drive = ntpath.splitdrive
else:
self.__split_drive = posixpath.splitdrive
def validate(self, value: PathType) -> None:
validate_pathtype(
value,
allow_whitespaces=False
if self.platform in [Platform.UNIVERSAL, Platform.WINDOWS]
else True,
)
self.validate_abspath(value)
_drive, value = self.__split_drive(str(value))
if not value:
return
filepath = os.path.normpath(value)
unicode_filepath = preprocess(filepath)
value_len = len(unicode_filepath)
if value_len > self.max_len:
raise InvalidLengthError(
"file path is too long: expected<={:d}, actual={:d}".format(self.max_len, value_len)
)
if value_len < self.min_len:
raise InvalidLengthError(
"file path is too short: expected>={:d}, actual={:d}".format(
self.min_len, value_len
)
)
self._validate_reserved_keywords(unicode_filepath)
unicode_filepath = unicode_filepath.replace("\\", "/")
for entry in unicode_filepath.split("/"):
if not entry or entry in (".", ".."):
continue
self.__fname_validator._validate_reserved_keywords(entry)
if self._is_universal() or self._is_windows():
self.__validate_win_filepath(unicode_filepath)
else:
self.__validate_unix_filepath(unicode_filepath)
def validate_abspath(self, value: PathType) -> None:
value = str(value)
is_posix_abs = posixpath.isabs(value)
is_nt_abs = ntpath.isabs(value)
err_object = ValidationError(
description=(
"an invalid absolute file path ({}) for the platform ({}).".format(
value, self.platform.value
)
+ " to avoid the error, specify an appropriate platform correspond"
+ " with the path format, or 'auto'."
),
platform=self.platform,
reason=ErrorReason.MALFORMED_ABS_PATH,
)
if any([self._is_windows() and is_nt_abs, self._is_linux() and is_posix_abs]):
return
if self._is_universal() and any([is_posix_abs, is_nt_abs]):
ValidationError(
description=(
"{}. expected a platform independent file path".format(
"POSIX absolute file path found"
if is_posix_abs
else "NT absolute file path found"
)
),
platform=self.platform,
reason=ErrorReason.MALFORMED_ABS_PATH,
)
if any([self._is_windows(), self._is_universal()]) and is_posix_abs:
raise err_object
drive, _tail = ntpath.splitdrive(value)
if not self._is_windows() and drive and is_nt_abs:
raise err_object
def __validate_unix_filepath(self, unicode_filepath: str) -> None:
match = _RE_INVALID_PATH.findall(unicode_filepath)
if match:
raise InvalidCharError(
self._ERROR_MSG_TEMPLATE.format(
invalid=findall_to_str(match), value=repr(unicode_filepath)
)
)
def __validate_win_filepath(self, unicode_filepath: str) -> None:
match = _RE_INVALID_WIN_PATH.findall(unicode_filepath)
if match:
raise InvalidCharError(
self._ERROR_MSG_TEMPLATE.format(
invalid=findall_to_str(match), value=repr(unicode_filepath)
),
platform=Platform.WINDOWS,
)
_drive, value = self.__split_drive(unicode_filepath)
if value:
match_reserved = self._RE_NTFS_RESERVED.search(value)
if match_reserved:
reserved_name = match_reserved.group()
raise ReservedNameError(
"'{}' is a reserved name".format(reserved_name),
reusable_name=False,
reserved_name=reserved_name,
platform=self.platform,
)
def validate_filepath(
file_path: PathType,
platform: Optional[str] = None,
min_len: int = 1,
max_len: Optional[int] = None,
check_reserved: bool = True,
) -> None:
"""Verifying whether the ``file_path`` is a valid file path or not.
Args:
file_path:
File path to validate.
platform:
Target platform name of the file path.
.. include:: platform.txt
min_len:
Minimum length of the ``file_path``. The value must be greater or equal to one.
Defaults to ``1``.
max_len:
Maximum length of the ``file_path`` length. If the value is |None|,
automatically determined by the ``platform``:
- ``Linux``: 4096
- ``macOS``: 1024
- ``Windows``: 260
- ``universal``: 260
check_reserved:
If |True|, check reserved names of the ``platform``.
Raises:
ValidationError (ErrorReason.INVALID_CHARACTER):
If the ``file_path`` includes invalid char(s):
|invalid_file_path_chars|.
The following characters are also invalid for Windows platform:
|invalid_win_file_path_chars|
ValidationError (ErrorReason.INVALID_LENGTH):
If the ``file_path`` is longer than ``max_len`` characters.
ValidationError:
If ``file_path`` include invalid values.
Example:
:ref:`example-validate-file-path`
See Also:
`Naming Files, Paths, and Namespaces - Win32 apps | Microsoft Docs
<https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file>`__
"""
FilePathValidator(
platform=platform, min_len=min_len, max_len=max_len, check_reserved=check_reserved
).validate(file_path)
def validate_file_path(file_path, platform=None, max_path_len=None):
# Deprecated
validate_filepath(file_path, platform, max_path_len)
def is_valid_filepath(
file_path: PathType,
platform: Optional[str] = None,
min_len: int = 1,
max_len: Optional[int] = None,
check_reserved: bool = True,
) -> bool:
"""Check whether the ``file_path`` is a valid name or not.
Args:
file_path:
A filepath to be checked.
Example:
:ref:`example-is-valid-filepath`
See Also:
:py:func:`.validate_filepath()`
"""
return FilePathValidator(
platform=platform, min_len=min_len, max_len=max_len, check_reserved=check_reserved
).is_valid(file_path)
def sanitize_filepath(
file_path: PathType,
replacement_text: str = "",
platform: Optional[str] = None,
max_len: Optional[int] = None,
check_reserved: bool = True,
normalize: bool = True,
) -> PathType:
"""Make a valid file path from a string.
To make a valid file path the function does:
- replace invalid characters for a file path within the ``file_path``
with the ``replacement_text``. Invalid characters are as follows:
- unprintable characters
- |invalid_file_path_chars|
- for Windows (or universal) only: |invalid_win_file_path_chars|
- Append underscore (``"_"``) at the tail of the name if sanitized name
is one of the reserved names by operating systems
(only when ``check_reserved`` is |True|).
Args:
file_path:
File path to sanitize.
replacement_text:
Replacement text for invalid characters.
Defaults to ``""``.
platform:
Target platform name of the file path.
.. include:: platform.txt
max_len:
Maximum length of the ``file_path`` length. Truncate the name if the ``file_path``
length exceedd this value. If the value is |None|,
``max_len`` will automatically determined by the ``platform``:
- ``Linux``: 4096
- ``macOS``: 1024
- ``Windows``: 260
- ``universal``: 260
check_reserved:
If |True|, sanitize reserved names of the ``platform``.
normalize:
If |True|, normalize the the file path.
Returns:
Same type as the argument (str or PathLike object):
Sanitized filepath.
Raises:
ValueError:
If the ``file_path`` is an invalid file path.
Example:
:ref:`example-sanitize-file-path`
"""
return FilePathSanitizer(
platform=platform, max_len=max_len, check_reserved=check_reserved, normalize=normalize
).sanitize(file_path, replacement_text)
def sanitize_file_path(file_path, replacement_text="", platform=None, max_path_len=None):
# Deprecated
return sanitize_filepath(file_path, platform, max_path_len)

View file

@ -0,0 +1,45 @@
"""
.. codeauthor:: Tsuyoshi Hombashi <tsuyoshi.hombashi@gmail.com>
"""
import re
from ._common import preprocess, validate_pathtype
from .error import InvalidCharError
__RE_INVALID_LTSV_LABEL = re.compile("[^0-9A-Za-z_.-]", re.UNICODE)
def validate_ltsv_label(label: str) -> None:
"""
Verifying whether ``label`` is a valid
`Labeled Tab-separated Values (LTSV) <http://ltsv.org/>`__ label or not.
:param label: Label to validate.
:raises pathvalidate.ValidationError:
If invalid character(s) found in the ``label`` for a LTSV format label.
"""
validate_pathtype(label, allow_whitespaces=False, error_msg="label is empty")
match_list = __RE_INVALID_LTSV_LABEL.findall(preprocess(label))
if match_list:
raise InvalidCharError(
"invalid character found for a LTSV format label: {}".format(match_list)
)
def sanitize_ltsv_label(label: str, replacement_text: str = "") -> str:
"""
Replace all of the symbols in text.
:param label: Input text.
:param replacement_text: Replacement text.
:return: A replacement string.
:rtype: str
"""
validate_pathtype(label, allow_whitespaces=False, error_msg="label is empty")
return __RE_INVALID_LTSV_LABEL.sub(replacement_text, preprocess(label))

View file

@ -0,0 +1,110 @@
"""
.. codeauthor:: Tsuyoshi Hombashi <tsuyoshi.hombashi@gmail.com>
"""
import re
from typing import Sequence
from ._common import ascii_symbols, preprocess, unprintable_ascii_chars
from .error import InvalidCharError
__RE_UNPRINTABLE = re.compile(
"[{}]".format(re.escape("".join(unprintable_ascii_chars))), re.UNICODE
)
__RE_SYMBOL = re.compile(
"[{}]".format(re.escape("".join(ascii_symbols + unprintable_ascii_chars))), re.UNICODE
)
def validate_unprintable(text: str) -> None:
# deprecated
match_list = __RE_UNPRINTABLE.findall(preprocess(text))
if match_list:
raise InvalidCharError("unprintable character found: {}".format(match_list))
def replace_unprintable(text: str, replacement_text: str = "") -> str:
# deprecated
try:
return __RE_UNPRINTABLE.sub(replacement_text, preprocess(text))
except (TypeError, AttributeError):
raise TypeError("text must be a string")
def validate_symbol(text: str) -> None:
"""
Verifying whether symbol(s) included in the ``text`` or not.
Args:
text:
Input text to validate.
Raises:
ValidationError (ErrorReason.INVALID_CHARACTER):
If symbol(s) included in the ``text``.
"""
match_list = __RE_SYMBOL.findall(preprocess(text))
if match_list:
raise InvalidCharError("invalid symbols found: {}".format(match_list))
def replace_symbol(
text: str,
replacement_text: str = "",
exclude_symbols: Sequence[str] = [],
is_replace_consecutive_chars: bool = False,
is_strip: bool = False,
) -> str:
"""
Replace all of the symbols in the ``text``.
Args:
text:
Input text.
replacement_text:
Replacement text.
exclude_symbols:
Symbols that exclude from the replacement.
is_replace_consecutive_chars:
If |True|, replace consecutive multiple ``replacement_text`` characters
to a single character.
is_strip:
If |True|, strip ``replacement_text`` from the beginning/end of the replacement text.
Returns:
A replacement string.
Example:
:ref:`example-sanitize-symbol`
"""
if exclude_symbols:
regexp = re.compile(
"[{}]".format(
re.escape(
"".join(set(ascii_symbols + unprintable_ascii_chars) - set(exclude_symbols))
)
),
re.UNICODE,
)
else:
regexp = __RE_SYMBOL
try:
new_text = regexp.sub(replacement_text, preprocess(text))
except TypeError:
raise TypeError("text must be a string")
if not replacement_text:
return new_text
if is_replace_consecutive_chars:
new_text = re.sub("{}+".format(re.escape(replacement_text)), replacement_text, new_text)
if is_strip:
new_text = new_text.strip(replacement_text)
return new_text

View file

@ -0,0 +1,68 @@
"""
.. codeauthor:: Tsuyoshi Hombashi <tsuyoshi.hombashi@gmail.com>
"""
from argparse import ArgumentTypeError
from ._common import PathType
from ._filename import sanitize_filename, validate_filename
from ._filepath import sanitize_filepath, validate_filepath
from .error import ValidationError
def validate_filename_arg(value: str) -> str:
if not value:
return ""
try:
validate_filename(value)
except ValidationError as e:
raise ArgumentTypeError(e)
return value
def validate_filepath_arg(value: str) -> str:
if not value:
return ""
try:
validate_filepath(value, platform="auto")
except ValidationError as e:
raise ArgumentTypeError(e)
return value
def sanitize_filename_arg(value: str) -> PathType:
if not value:
return ""
return sanitize_filename(value)
def sanitize_filepath_arg(value: str) -> PathType:
if not value:
return ""
return sanitize_filepath(value, platform="auto")
def filename(value: PathType) -> PathType: # pragma: no cover
# Deprecated
try:
validate_filename(value)
except ValidationError as e:
raise ArgumentTypeError(e)
return sanitize_filename(value)
def filepath(value: PathType) -> PathType: # pragma: no cover
# Deprecated
try:
validate_filepath(value)
except ValidationError as e:
raise ArgumentTypeError(e)
return sanitize_filepath(value)

View file

@ -0,0 +1,74 @@
"""
.. codeauthor:: Tsuyoshi Hombashi <tsuyoshi.hombashi@gmail.com>
"""
import click
from ._common import PathType
from ._filename import sanitize_filename, validate_filename
from ._filepath import sanitize_filepath, validate_filepath
from .error import ValidationError
def validate_filename_arg(ctx, param, value) -> str:
if not value:
return ""
try:
validate_filename(value)
except ValidationError as e:
raise click.BadParameter(str(e))
return value
def validate_filepath_arg(ctx, param, value) -> str:
if not value:
return ""
try:
validate_filepath(value)
except ValidationError as e:
raise click.BadParameter(str(e))
return value
def sanitize_filename_arg(ctx, param, value) -> PathType:
if not value:
return ""
return sanitize_filename(value)
def sanitize_filepath_arg(ctx, param, value) -> PathType:
if not value:
return ""
return sanitize_filepath(value)
def filename(ctx, param, value): # pragma: no cover
# Deprecated
if not value:
return None
try:
validate_filename(value)
except ValidationError as e:
raise click.BadParameter(str(e))
return sanitize_filename(value)
def filepath(ctx, param, value): # pragma: no cover
# Deprecated
if not value:
return None
try:
validate_filepath(value)
except ValidationError as e:
raise click.BadParameter(str(e))
return sanitize_filepath(value)

View file

@ -0,0 +1,155 @@
"""
.. codeauthor:: Tsuyoshi Hombashi <tsuyoshi.hombashi@gmail.com>
"""
import enum
from typing import Optional, cast
from ._common import Platform
@enum.unique
class ErrorReason(enum.Enum):
"""
Validation error reasons.
"""
FOUND_ABS_PATH = "FOUND_ABS_PATH" #: found an absolute path when expecting a file name
NULL_NAME = "NULL_NAME" #: empty value
INVALID_CHARACTER = "INVALID_CHARACTER" #: found invalid characters(s) in a value
INVALID_LENGTH = "INVALID_LENGTH" #: found invalid string length
MALFORMED_ABS_PATH = "MALFORMED_ABS_PATH" #: found invalid absolute path format
RESERVED_NAME = "RESERVED_NAME" #: found a reserved name by a platform
class ValidationError(ValueError):
"""
Exception class of validation errors.
.. py:attribute:: reason
The cause of the error.
Returns:
:py:class:`~pathvalidate.error.ErrorReason`:
"""
@property
def platform(self) -> Platform:
return self.__platform
@property
def reason(self) -> Optional[ErrorReason]:
return self.__reason
@property
def description(self) -> str:
return self.__description
@property
def reserved_name(self) -> str:
return self.__reserved_name
@property
def reusable_name(self) -> bool:
return self.__reusable_name
def __init__(self, *args, **kwargs):
self.__platform = kwargs.pop("platform", None)
self.__reason = kwargs.pop("reason", None)
self.__description = kwargs.pop("description", None)
self.__reserved_name = kwargs.pop("reserved_name", None)
self.__reusable_name = kwargs.pop("reusable_name", None)
try:
super().__init__(*args[0], **kwargs)
except IndexError:
super().__init__(*args, **kwargs)
def __str__(self) -> str:
item_list = []
if Exception.__str__(self):
item_list.append(Exception.__str__(self))
if self.reason:
item_list.append("reason={}".format(cast(ErrorReason, self.reason).value))
if self.platform:
item_list.append("target-platform={}".format(self.platform.value))
if self.description:
item_list.append("description={}".format(self.description))
if self.__reusable_name is not None:
item_list.append("reusable_name={}".format(self.reusable_name))
return ", ".join(item_list).strip()
def __repr__(self, *args, **kwargs):
return self.__str__(*args, **kwargs)
class NullNameError(ValidationError):
"""
Exception raised when a name is empty.
"""
def __init__(self, *args, **kwargs) -> None:
kwargs["reason"] = ErrorReason.NULL_NAME
super().__init__(args, **kwargs)
class InvalidCharError(ValidationError):
"""
Exception raised when includes invalid character(s) within a string.
"""
def __init__(self, *args, **kwargs) -> None:
kwargs["reason"] = ErrorReason.INVALID_CHARACTER
super().__init__(args, **kwargs)
class InvalidLengthError(ValidationError):
"""
Exception raised when a string too long/short.
"""
def __init__(self, *args, **kwargs) -> None:
kwargs["reason"] = ErrorReason.INVALID_LENGTH
super().__init__(args, **kwargs)
class ReservedNameError(ValidationError):
"""
Exception raised when a string matched a reserved name.
"""
def __init__(self, *args, **kwargs) -> None:
kwargs["reason"] = ErrorReason.RESERVED_NAME
super().__init__(args, **kwargs)
class ValidReservedNameError(ReservedNameError):
"""
Exception raised when a string matched a reserved name.
However, it can be used as a name.
"""
def __init__(self, *args, **kwargs) -> None:
kwargs["reusable_name"] = True
super().__init__(args, **kwargs)
class InvalidReservedNameError(ReservedNameError):
"""
Exception raised when a string matched a reserved name.
Moreover, the reserved name is invalid as a name.
"""
def __init__(self, *args, **kwargs) -> None:
kwargs["reusable_name"] = False
super().__init__(args, **kwargs)

View file

View file

@ -402,9 +402,9 @@ def _get_playListVersion_from_xml(playlist, xml):
Raises PlaylistError if unsuccessful Raises PlaylistError if unsuccessful
""" """
playlist.version = utils.cast(int, try:
xml.get('%sVersion' % playlist.kind)) playlist.version = int(xml.get('%sVersion' % playlist.kind))
if playlist.version is None: except (AttributeError, TypeError):
raise PlaylistError('Could not get new playlist Version for playlist ' raise PlaylistError('Could not get new playlist Version for playlist '
'%s' % playlist) '%s' % playlist)
@ -416,6 +416,8 @@ def get_playlist_details_from_xml(playlist, xml):
Raises PlaylistError if something went wrong. Raises PlaylistError if something went wrong.
""" """
if xml is None:
raise PlaylistError('No playlist received for playlist %s' % playlist)
playlist.id = utils.cast(int, playlist.id = utils.cast(int,
xml.get('%sID' % playlist.kind)) xml.get('%sID' % playlist.kind))
playlist.version = utils.cast(int, playlist.version = utils.cast(int,
@ -703,8 +705,8 @@ def delete_playlist_item_from_PMS(playlist, pos):
playlist.items[pos].id, playlist.items[pos].id,
playlist.repeat), playlist.repeat),
action_type="DELETE") action_type="DELETE")
_get_playListVersion_from_xml(playlist, xml)
del playlist.items[pos] del playlist.items[pos]
_get_playListVersion_from_xml(playlist, xml)
# Functions operating on the Kodi playlist objects ########## # Functions operating on the Kodi playlist objects ##########

View file

@ -96,6 +96,13 @@ class Base(object):
""" """
return self.xml.get('type') return self.xml.get('type')
@property
def subtype(self):
"""
Returns the subtype of media, e.g. 'clip' as string or None.
"""
return self.xml.get('subtype')
@property @property
def section_id(self): def section_id(self):
self.check_db() self.check_db()

View file

@ -369,7 +369,8 @@ class Media(object):
force_check : Will always try to check validity of path force_check : Will always try to check validity of path
Will also skip confirmation dialog if path not found Will also skip confirmation dialog if path not found
folder : Set to True if path is a folder folder : Set to True if path is a folder
omit_check : Will entirely omit validity check if True omit_check : Will entirely omit validity check if True. Will
be superseded by force_check!
""" """
if path is None: if path is None:
return return
@ -385,7 +386,13 @@ class Media(object):
path = 'smb:' + path.replace('\\', '/') path = 'smb:' + path.replace('\\', '/')
if app.SYNC.escape_path: if app.SYNC.escape_path:
path = utils.escape_path(path, app.SYNC.escape_path_safe_chars) path = utils.escape_path(path, app.SYNC.escape_path_safe_chars)
if (app.SYNC.path_verified and not force_check) or omit_check: if force_check:
pass
elif omit_check:
return path
elif not app.SYNC.check_media_file_existence:
return path
elif app.SYNC.path_verified:
return path return path
# exist() needs a / or \ at the end to work for directories # exist() needs a / or \ at the end to work for directories

View file

@ -92,7 +92,7 @@ class MyHandler(BaseHTTPRequestHandler):
self.send_header('Content-Length', len(body)) self.send_header('Content-Length', len(body))
self.send_header('Connection', "close") self.send_header('Connection', "close")
self.end_headers() self.end_headers()
self.wfile.write(body) self.wfile.write(body.encode('utf-8'))
self.wfile.close() self.wfile.close()
except Exception: except Exception:
pass pass

View file

@ -26,7 +26,7 @@ import xbmc
import xbmcaddon import xbmcaddon
import xbmcgui import xbmcgui
from . import path_ops, variables as v from . import pathvalidate, path_ops, variables as v
LOG = getLogger('PLEX.utils') LOG = getLogger('PLEX.utils')
@ -422,29 +422,10 @@ def valid_filename(text):
""" """
Return a valid filename after passing it in [unicode]. Return a valid filename after passing it in [unicode].
""" """
# Get rid of all whitespace except a normal space return pathvalidate.sanitize_filename(text,
text = re.sub(r'(?! )\s', '', text) replacement_text='_',
# ASCII characters 0 to 31 (non-printable, just in case) platform='auto',
text = re.sub(u'[\x00-\x1f]', '', text) max_len=248)
if v.DEVICE == 'Windows':
# Whitespace at the end of the filename is illegal
text = text.strip()
# Dot at the end of a filename is illegal
text = re.sub(r'\.+$', '', text)
# Illegal Windows characters
text = re.sub(r'[/\\:*?"<>|\^]', '', text)
elif v.DEVICE == 'MacOSX':
# Colon is illegal
text = re.sub(r':', '', text)
# Files cannot begin with a dot
text = re.sub(r'^\.+', '', text)
else:
# Linux
text = re.sub(r'/', '', text)
# Ensure that filename length is at most 255 chars (including 3 chars for
# filename extension and 1 dot to separate the extension)
text = text[:min(len(text), 251)]
return text
def escape_html(string): def escape_html(string):

View file

@ -84,7 +84,7 @@ COMPANION_PORT = int(_ADDON.getSetting('companionPort'))
PKC_MACHINE_IDENTIFIER = None PKC_MACHINE_IDENTIFIER = None
# Minimal PKC version needed for the Kodi database - otherwise need to recreate # Minimal PKC version needed for the Kodi database - otherwise need to recreate
MIN_DB_VERSION = '2.6.8' MIN_DB_VERSION = '3.1.3'
# Supported databases - version numbers in tuples should decrease # Supported databases - version numbers in tuples should decrease
SUPPORTED_VIDEO_DB = { SUPPORTED_VIDEO_DB = {
@ -668,14 +668,10 @@ PLEX_STREAM_TYPE_FROM_STREAM_TYPE = {
# Encoding to be used for our m3u playlist files # Encoding to be used for our m3u playlist files
# m3u files do not have encoding specified by definition, unfortunately. # m3u files do not have encoding specified by definition, unfortunately.
if DEVICE == 'Windows': # The filesystem encoding is generally utf-8
M3U_ENCODING = 'mbcs' # For Windows, it can be either utf-8 or mbcs
else: # See https://docs.python.org/3/library/sys.html#sys.getfilesystemencoding
M3U_ENCODING = sys.getfilesystemencoding() M3U_ENCODING = sys.getfilesystemencoding()
if (not M3U_ENCODING or
M3U_ENCODING == 'ascii' or
M3U_ENCODING == 'ANSI_X3.4-1968'):
M3U_ENCODING = 'utf-8'
def database_paths(): def database_paths():

View file

@ -64,6 +64,7 @@
<setting id="dbSyncScreensaver" type="bool" label="39062" default="false" /><!--Sync when screensaver is deactivated--> <setting id="dbSyncScreensaver" type="bool" label="39062" default="false" /><!--Sync when screensaver is deactivated-->
<setting id="dbSyncIndicator" label="30507" type="bool" default="true" /><!-- show syncing progress --> <setting id="dbSyncIndicator" label="30507" type="bool" default="true" /><!-- show syncing progress -->
<setting id="playstate_sync_indicator" label="30523" type="bool" default="false" visible="eq(-1,true)" subsetting="true"/><!-- Also show sync progress for playstate and user data --> <setting id="playstate_sync_indicator" label="30523" type="bool" default="false" visible="eq(-1,true)" subsetting="true"/><!-- Also show sync progress for playstate and user data -->
<setting id="check_media_file_existence" type="bool" label="39075" default="true" /><!--Verify access to media files while synching -->
<setting id="syncThreadNumber" type="slider" label="39003" default="10" option="int" range="1,1,30"/><!-- Number of simultaneous download threads --> <setting id="syncThreadNumber" type="slider" label="39003" default="10" option="int" range="1,1,30"/><!-- Number of simultaneous download threads -->
<setting id="limitindex" type="slider" label="30515" default="200" option="int" range="50,50,1000"/><!-- Maximum items to request from the server at once --> <setting id="limitindex" type="slider" label="30515" default="200" option="int" range="50,50,1000"/><!-- Maximum items to request from the server at once -->
<setting type="lsep" label="$LOCALIZE[136]" /><!-- Playlists --> <setting type="lsep" label="$LOCALIZE[136]" /><!-- Playlists -->