Parental Control Channel for PMS

Ok, after my basic understanding of the Plex framework, I was led to believe that there would be no other option for Parental Control except by creating user login for Plex and sharing certain content. If a password protected movie section was available, the password would unlock the section on the server hence not restrict any other client on the network from accessing it.

 

I am sharing a workaround which is a dirty hack and should be followed at your own risk. I was able to lock/unlock a section via my Mac Mini for a while now but was always irate that I had to log in to my PMS machine to do it. That was till I stumbled across the Plugin Channel framework. Alas, I can now lock/unlock my Adult movies via a channel from any of my plex clients.

 

Currently, this works for PMS on Mac OS X, I however have used the same theory of editing the PMS database on Linux and Windows in the past. This is my first Channel plugin, appreciate dev’s helping me clean it up and suggesting, for now, it works!

 

1) Visit the Lock plug-in generator page, follow instructions there

 

2) Enjoy!

 

Note: Once you unlock, refresh (if possible like in the iOS app) the sections or restart (PHT for windows) the client!

Currently, unlocking via Plex Web requires you to type the password in the search field once you are inside the lock channel, maybe due to the use of InputDirectoryObject which is meant for search fields.

 

IMPORTANT!:After installing and using the lock plugin, should you decide to delete the plugin, for whatever reason (for example to change the password and re-generate the lock plugin) pl. remember to unlock section(s) before deleting the lock plugin, Lock.command and Unlock.command!

 

Note: This plugin was enhanced greatly with the help of Mikedm139, without whoom the plugin would not be so user friendly. Thank you Mikedm139, sincerely appreciate it!

Huge thanks to dane22 for testing the channel!

Nice work. Very neat application.

A couple notes:

  1. It looks like you're trying to set a Prefs value in line #30 in ValidatePrefs(). That won't work for two reasons: First, it's using the "==" test rather than the "=" assignment operator. Second and more important, the plugin framework forbids assigning pref values from the plugin code. That That may explain why you're running problems with ValidatePrefs().
  2. Not sure if you meant to type reset.dict() but I wouldn't expect that to work. AFAIK, "Dict.reset()" is functional but I might be able to propose a scenario where it would be unnecessary. 
  3. The external command files are helpful but you'll have a much easier time supporting multiple-platforms and less "hands-on" users if the commands are integrated in the channel bundle. Python's subprocess.call() method should allow you to achieve that.

Suggestions:

  • I'll fork your GitHub repo and send you a Pull Request with my suggestions for further discussion.

Mikedm139, thank you so much for your kind words and constructive criticism. I sincerely appreciate it!

#1 Thank you, I understand now the limitations and the mistake I made in the ValidatePrefs()

#2 Would appreciate it if you could propose a better way

#3 Yes, I understood that. In fact I managed to connect to the database using:

import sqlite3

conn = sqlite3.connect(~/Library/Application\ Support/Plex\ Media\ Server/Plug-in\ Support/Databases/com.plexapp.plugins.library.db’)

Log.Info(‘Opened database successfully’)

conn.execute()

I am on OS X 10.6.8 and hence the built in SQLite3 version of OS X 10.6.8 didn’t permit me to make changes to the Plex database. I looked around to see if there was a solution which would allow me to use the PMS libsqlite3.0.dylib (Or something similar, I really am out of my depth on this). I then concluded that one would appreciate to Lock and Unlock from the PMS machine desktop as well. The built in Python 2.6 on OS X 10.6.8 does not have the required version library of Sqlite3 as well :/

I totally understand that cross platform would definitely be the way to go. Would really appreciate it if you could send me a pull request with your suggestions (new to github as well so will try my best).

Thank you!

___/\_____/\____, thank you for posting in the other thread that you started this channel.  I do like your idea and since it is the only solution so far I will likely use it for myself.  As I understand it your solution will unlock a media section in the main Plex menu and you have to manually lock it again after.  Your solution is close to what I was thinking but not exactly.  I was thinking of a channel that would be password/pin protected and once in the channel you would see your 'Private' folder(s) and play the media in the channel similar to the Crackle channel.  But I like your solution as well I'm concerned about the screen captures and how Plex will display media screen captures in the background.  The last time I added my 'Private' folders to the Plex library I was seeing images in the Plex backgrounds that I did not want to see!    I'll have to give it a try this week and report back in this thread.  Oh also my PMS and Plex clients are Windows and Android.

clarenceb5, your welcome :) ! I created this solution for myself and just decided to share it with everyone for:

1) The hope of a more refined approach as opposed to mine

2) Help from experienced developers to offer their views

3) Share the joy ;)

All your existing metadata, backgrounds and all other section related data will remain intact. Once you lock the section, nothing should show i.e. even the "recently added" section in plex web should not show the recently added videos to the section you are hiding.

So long as the PMS machine is a Mac, the client should not matter! It should work!

Enjoy!

Mikedm139, thank you so much for your kind words and constructive criticism. I sincerely appreciate it!

#1 Thank you, I understand now the limitations and the mistake I made in the ValidatePrefs()

#2 Would appreciate it if you could propose a better way

#3 Yes, I understood that. In fact I managed to connect to the database using:

import sqlite3

conn = sqlite3.connect(~/Library/Application\ Support/Plex\ Media\ Server/Plug-in\ Support/Databases/com.plexapp.plugins.library.db’)

Log.Info(‘Opened database successfully’)

conn.execute()

I am on OS X 10.6.8 and hence the built in SQLite3 version of OS X 10.6.8 didn’t permit me to make changes to the Plex database. I looked around to see if there was a solution which would allow me to use the PMS libsqlite3.0.dylib (Or something similar, I really am out of my depth on this). I then concluded that one would appreciate to Lock and Unlock from the PMS machine desktop as well. The built in Python 2.6 on OS X 10.6.8 does not have the required version library of Sqlite3 as well :/

I totally understand that cross platform would definitely be the way to go. Would really appreciate it if you could send me a pull request with your suggestions (new to github as well so will try my best).

Thank you!

I've never messed with sqlite, so this is new for me too. I should have something together for further discussion by the weekend at the latest.

I believe that the PMS has been standardized across platforms to include a built-in distribution of python2.7. So as long as we can access sqlite3 as an import, we're in even better shape.

It occurs to me that editing the database while other DB operations are active might have rather nasty side effects...

As I mentioned above, I'm a sqlite noob. Is this something that we should be concerned about? Would it be advisable to try to figure out a way to cancel ongoing library updates etc. while we have the database open?

I've never messed with sqlite, so this is new for me too. I should have something together for further discussion by the weekend at the latest.

I believe that the PMS has been standardized across platforms to include a built-in distribution of python2.7. So as long as we can access sqlite3 as an import, we're in even better shape.

Thank you so much for taking the time to suggest and help me develop this code even further, sincerely appreciate it!

Yes Sir, it would be good news indeed, more PMS compatible if the inbuilt libraries could be used. It however then may not be so cross-platform as per the solution you suggested earlier. I take it PMS has slightly different accessibility protocols depending on the OS. If it was part of the plugin framework though, should be some standardization I presume. Very out of my depth on this, hence for now just using external commands! OS X 10.6.8 however, does need a little different .command/approach.

It occurs to me that editing the database while other DB operations are active might have rather nasty side effects...

As I mentioned above, I'm a sqlite noob. Is this something that we should be concerned about? Would it be advisable to try to figure out a way to cancel ongoing library updates etc. while we have the database open?

This is true in theory! Hence I said in the start of the post, try it at your own risk. It all depends on how PMS behaves. Think of a database like gmail being accessed by millions at any given point of time (just a presumption) they have their DBA’s(Database administrators) and what not and the coding is in a different language, however the database structure is designed to take the beating.

Our PMS is a very basic sqlite3 db, those used mostly in phone development/databases. It does the job! I have been using this fix for 2 years now, first linux, then windows and now mac ; ) the db has never crashed. I don’t however, unnecessarily test crash scenario’s.

The favorite should be, if some plex client is watching a video of the section that suddenly gets locked/hidden, what happens? Boo :D Nothing happens, you continue to watch, but when you go back to the menu, it’s gone, Magic!

The simple sql query’s that I use is not touching the stuff that (if you mess with) really causes problems, it merely just tells all the clients and PMS, there is no section, so cuts off the whole PMS process at the neck. This obviously is based on my brief study of the db. I could be very wrong. I still however have never faced any problems till date.

If you’d like to learn a lil more about the db here is a post I found most interesting.

PMS does not obtain a reserved lock on the database when using it, the architect can easily do so in the future though, just out of spite of this plugin lol, which I doubt again coz plex is a loving hippy kind of community that encourages hacks but silently smirks at it. More to the point, so long as the query does not conflict with what PMS is doing at that time, nothing bad should happen and PMS would never go to re-create a section. Either the section exists or it does not, in our case when the section is locked as far as PMS is concerned, it probably says “who dat?”, “is someone there?”  Hehe. When we unlock PMS probably says “Oh, it’s you, I remember your uuid, I created you!, lets update your library if you like!”

It’s like deleting the name field in a address book and then searching the address book via name, it just won’t show up, all the corresponding data is there but the system has no way of knowing that it even exists till you put back the name. Name being the primary_key that the addressbook uses to co-relate the corresponding data.

Thanks for the basic sqlite DB intro. I'll have to bookmark that link and refer back to it in the future :)

I've sent a pull request on GitHub with some untested changes. If you're feeling adventurous and want to test out my changes (before I get a chance), you can run the following git commands from within your local "Lock" repository to checkout my changes without overwriting your own master branch.

git remote add mikedm139 https://github.com/mikedm139/Lock.git
git fetch mikedm139
git checkout -b test mikedm139/master

One of the changes I will suggest (in future) is to convert the git repository into the .bundle format to make things easier and more in line with the "standard" approach. For now, you'll just need copy the Lock directory to a Lock.bundle directory for testing.

Wow! very nice.

Mikedm139, all I can say is WOW! Thank you so much! There were minor glitches (which I guess you'll find out when you test) but overall, WOW! I had always envisioned such a solution, never thought it possible. Thank you so much for an awesome solution!

Thoughts:

I couldn't however fully test the changes cause I am on OS X 10.6.8 but got to the point where I could connect to the db, after that executing querys just fails due to version mismatch of sqlite3(nothing to do with the plugin code), I have learned that PMS uses respective OS X's python and the built-in version of python's sqlite3. In short, when I "import sqlite3" the version of Python is 2.7.6 and SQLite3 is 3.6.12. PMS uses I think(not sure) Sqlite3 3.8.2 or higher to write to the PMS database via libsqlite3.0.dylib (not sure). OS X 10.6.8 ships with Python 2.6, which I upgraded to 2.7 to use PlexConnect. I could update to a higher version of Python but don't want to have 3 versions in parallel and a path nightmare.

Should ideally work for any OS X above 10.7.5 (I don’t think it would work for OS X 10.6.8 running Python 2.6 or OS X 10.7.5 running Python 2.7, the corresponding sqlite3 versions don’t match with that used by PMS (the libraries it uses to talk to the db in PMS's internal framework))

I am still trying to wrap my head around why PMS would use one version(internal PMS framework version of Sqlite3) to write to the db for all it's tasks and then use the OS X version(python's sqlite3) for other purposes(like the plugin framework) and how it is doing so. I guess it is to prevent exactly what ppl. like us are trying lol.

Very cross-platform : ), windows users may have to install sqlite3 3.8.2 and Python 3, not sure, theory!

The technique is awesome, I however have not fully understood "dict", is each plugin given its own space in dict or is it like a pool for all plugins to use? I ask this cause I saw that the sql query is being saved in dict(the query that will help restore the section detail when unlocked) before the hide/lock process is executed(which deletes the row identifying the section). If another plugin were to reset or clear dict in some way, would the lock plugin still retain the sql query used to restore/unlock the section(s)?. If dict will still have all the data, then there should be no problem :) Otherwise the novice user would never be able to get back the section lost/locked and risk crippling the database (The fact that you have coded such an advanced plugin tells me you've already thought of this, just checking due to the lack of my knowledge on the subject)

Loved the way you got the path of the DB : ) very, very cool! Makes it cross platform.

Overall, can’t wait for you to test!

(Pl. note I updated the unlock command yesterday cause it was using a method of calling the sqlite3 db by using Sqlite3 3.8.2 residing in my Applications folder in OS X 10.6.8, It is the current workaround I use to edit the PMS db via Terminal line)

Conclusions:

Absolutely fantastic!

Suggestion:

I would like to transfer the git ownership of the repo to you(not that you need it ;) ), since you obviously have put so much work into the “correct way” of approaching the solution to lock/unlock. I’m being sincere, I know when I’m in over my head lol, it took me a day to wrap my head around the awesome code you’ve written and cannot in good conscience feel I will do justice in maintaining such a repo. It is my humble request that you start a new thread and post it as your repo, this thread can be closed or left open for hands-on users still on OS X 10.6.8(I will ammend my initial post pointing people to your post)

P.S: This query “UPDATE metadata_items SET added_at='' WHERE library_section_id=”  used as one of the queries to lock the db, actually is intended to change the timestamp of the “added_at” so as to not let the “recently added” section show any of the videos recently added in the section that is getting locked in Plex Web and other clients.

I will always be around to brainstorm about the query’s/db and test(if possible) : )

Thank you so much for taking this plugin/thought to another level!

Mikedm139, all I can say is WOW! Thank you so much! There were minor glitches (which I guess you'll find out when you test) but overall, WOW! I had always envisioned such a solution, never thought it possible. Thank you so much for an awesome solution!

Thoughts:

I couldn't however fully test the changes cause I am on OS X 10.6.8 but got to the point where I could connect to the db, after that executing querys just fails due to version mismatch of sqlite3(nothing to do with the plugin code), I have learned that PMS uses respective OS X's python and the built-in version of python's sqlite3. In short, when I "import sqlite3" the version of Python is 2.7.6 and SQLite3 is 3.6.12. PMS uses I think(not sure) Sqlite3 3.8.2 or higher to write to the PMS database via libsqlite3.0.dylib (not sure). OS X 10.6.8 ships with Python 2.6, which I upgraded to 2.7 to use PlexConnect. I could update to a higher version of Python but don't want to have 3 versions in parallel and a path nightmare.

Should ideally work for any OS X above 10.7.5 (I don’t think it would work for OS X 10.6.8 running Python 2.6 or OS X 10.7.5 running Python 2.7, the corresponding sqlite3 versions don’t match with that used by PMS (the libraries it uses to talk to the db in PMS's internal framework))

I am still trying to wrap my head around why PMS would use one version(internal PMS framework version of Sqlite3) to write to the db for all it's tasks and then use the OS X version(python's sqlite3) for other purposes(like the plugin framework) and how it is doing so. I guess it is to prevent exactly what ppl. like us are trying lol.

Very cross-platform : ), windows users may have to install sqlite3 3.8.2 and Python 3, not sure, theory!

I'll have to do a little more research on what version(s) of sqlite are available cross-platform and how the channel framework accesses/implements it. I thought it should all be the same now.

The technique is awesome, I however have not fully understood "dict", is each plugin given its own space in dict or is it like a pool for all plugins to use? I ask this cause I saw that the sql query is being saved in dict(the query that will help restore the section detail when unlocked) before the hide/lock process is executed(which deletes the row identifying the section). If another plugin were to reset or clear dict in some way, would the lock plugin still retain the sql query used to restore/unlock the section(s)?. If dict will still have all the data, then there should be no problem  :) Otherwise the novice user would never be able to get back the section lost/locked and risk crippling the database (The fact that you have coded such an advanced plugin tells me you've already thought of this, just checking due to the lack of my knowledge on the subject)

Loved the way you got the path of the DB : ) very, very cool! Makes it cross platform.

The Dict is plugin-specific persistent storage so other plugins have no effect on the storage for this one. It's pretty persistent to the point of requiring a user to specifically dig into the plugin support directory to delete the Dict file for this plugin or wipe the entire PMS install to delete the stored values.

Suggestion:

I would like to transfer the git ownership of the repo to you(not that you need it  ;) ), since you obviously have put so much work into the “correct way” of approaching the solution to lock/unlock. I’m being sincere, I know when I’m in over my head lol, it took me a day to wrap my head around the awesome code you’ve written and cannot in good conscience feel I will do justice in maintaining such a repo. It is my humble request that you start a new thread and post it as your repo, this thread can be closed or left open for hands-on users still on OS X 10.6.8(I will ammend my initial post pointing people to your post)

I'm not averse taking over control of the GitHub repo. You can achieve the transfer of ownership in one of a couple ways. The quick and easy way would be to delete your copy of the GitHub repo from your GitHub account. By default, my fork would then become the "primary" fork. If you wish to contribute code in the future, you would just fork my repo and send me a pull request with the changes. FWIW, this is the way plugin ownership is generally transferred when they are added to the Plex Channel Directory.

 

P.S: This query “UPDATE metadata_items SET added_at='' WHERE library_section_id=”  used as one of the queries to lock the db, actually is intended to change the timestamp of the “added_at” so as to not let the “recently added” section show any of the videos recently added in the section that is getting locked in Plex Web and other clients.

 

I will always be around to brainstorm about the query’s/db and test(if possible) : )

 

Thank you so much for taking this plugin/thought to another level!

 
 
I kinda figured that it had something to do with that. I tried to set things up so that the original "created" and "last updated" dates from the database would be preserved and re-used in the plugin. I was a little uncertain as to what date format should be used. The format in the XML returned from PMS is a time-stamp but your code was using a more human readable date. I'm sure with some testing I can figure it out.
 
I was also a little confused (as you may have noticed in my code comments) about whether the section types are mapped to an int value in the sqlite DB or if passing the same string from the PMS-sections XML would work. I saw that your command was passing a value of "1" for the section type.

I'll have to do a little more research on what version(s) of sqlite are available cross-platform and how the channel framework accesses/implements it. I thought it should all be the same now.

Thank you, this I feel may well be the cornerstone of cross-platform compatibility.

The Dict is plugin-specific persistent storage so other plugins have no effect on the storage for this one. It's pretty persistent to the point of requiring a user to specifically dig into the plugin support directory to delete the Dict file for this plugin or wipe the entire PMS install to delete the stored values.

Thank you, was wondering if a stored sql procedure would be required, that takes in sectionid and uuid and then stores all the values of the section that is to be locked in a dummy table structured as the library_section table, which can later be used to re-populate the library_sections table on unlock. Adding tables, stored procedures to the PMS db may not be wise. Thank you for sharing info on Dict, looks like it's perfect for storing data.

I’m not averse taking over control of the GitHub repo. You can achieve the transfer of ownership in one of a couple ways. The quick and easy way would be to delete your copy of the GitHub repo from your GitHub account. By default, my fork would then become the “primary” fork. If you wish to contribute code in the future, you would just fork my repo and send me a pull request with the changes. FWIW, this is the way plugin ownership is generally transferred when they are added to the Plex Channel Directory.

Will do, thank you :)

I kinda figured that it had something to do with that. I tried to set things up so that the original "created" and "last updated" dates from the database would be preserved and re-used in the plugin. I was a little uncertain as to what date format should be used. The format in the XML returned from PMS is a time-stamp but your code was using a more human readable date. I'm sure with some testing I can figure it out.
 
I was also a little confused (as you may have noticed in my code comments) about whether the section types are mapped to an int value in the sqlite DB or if passing the same string from the PMS-sections XML would work. I saw that your command was passing a value of "1" for the section type.

Yeah, I noticed this too, I was staring at it and blinking for 5 minutes :P The date used by XML is UNIX date format i.e. a 10 Digit format, it can be converted using the following:

1) via SQL

CONVERT(VARCHAR(DATEADD(SS,{<10DigitXMLDATE>},'19700101'),109))

OR

CAST(DATEADD(yy, +10, '"+<10DigitXMLDATE>+"') AS DATE)"

2) via python

import time
time.strftime("%D %H:%M", time.localtime(int("<10DigitXMLDATE>")))

OR

import datetime
temp = datetime.datetime.fromtimestamp(<10DigitXMLDATE>).strftime('%Y-%m-%d %H:%M:%S')
print temp

Haven't checked any of the above though coz I'm not sure what PMS uses to encode/decode the date. That PMS method would be the way to go.

----

Yep, you got it right, the int value corresponds to the type of the section, however the XML data does not show the type in int but a String. Eg:- type "movie" is 1, type "show" is 2. Since the number of types is limited at the moment(photo, show, movie, music) it could be hardcoded (what I was doing to lock only movie type sections). The better way to do it would be to write a simple sql join to pull the correct data required. This would enable locking of TV Shows, Photos or any “type” of section. Will have a look at the PMS db scheme and report back by updating this post if I find a co-relation, I doubt it coz I think it is part of how PMS interprets the data, not sure.

Thank you for your time :) I feel myself getting pulled into PlexLand :D already perplexed :)

---- USE AT YOUR OWN RISK ----

I've done some testing and updating. It's currently working as expected on my setup. I'm curious to hear about results from other people. I would strongly suggest making a backup of your com.plexapp.plugins.library.db file before testing this plugin.

Download from GitHub, unzip and install into your plug-ins directory.

https://github.com/mikedm139/Lock.bundle/archive/master.zip

Note:

If you're using the Plex/Web app to test, you will run into troubles because the plugin uses several "InputDirectoryObjects" to request user input. The /Web app assigns the first InputDirectoryObject to the "Search" bar and ignores the other(s). Once the plugin is setup, this can work in your favour (stealth-wise) because the only thing that shows in the plugin via the web-app is the "Lock" option. Users in the know can enter their password into the Search bar to unlock hidden their section(s). However, the option to change settings (which should be noted, are not accessible via the Prefs menu), is inaccessible via the web client.

If anyone has feedback or comments, this is a great place to post them :)

Can't run on QNAP.

Say's: "ImportError: No module named _sqlite3"

Take a peak here:

https://forums.plex.tv/topic/39712-list-unmatched-media/?p=441065

as well as some of the following posts.

I once requested that the missing files could get included with Plex, but was sadly turned down.

/T

Can't run on QNAP.

Say's: "ImportError: No module named _sqlite3"

Take a peak here:

https://forums.plex.tv/topic/39712-list-unmatched-media/?p=441065

as well as some of the following posts.

I once requested that the missing files could get included with Plex, but was sadly turned down.

/T

Interesting. I'll have to look at a workaround.

Interesting. I'll have to look at a workaround.

If you find one, then I'm all ears.....

Got a bunch of ideas here, but keeps getting blocked by not having access to the darn database

And sadly, a QNAP, as well as other NAS boxes, are simply reported as Linux2, so providing the missing files in the plugin is IMHO a no-go, as well as could lead to version mismatch with the so file

/T

Ya, that's not ideal.

Ya, that's not ideal.

Just hoping you got more buttons to push than I have  :lol:

Cuz. I really would love to see this included with PMS, and still fails to see why not, unless Plex Inc. are afraid that 3.Party might cause DB curruptions

/T

How about this:

Host a repository on like DropBox with the missing files, and then during the first run, ask the user to select his back-end, if we can't auto-detect.

If we can auto-detect, then we simply download the darn thing, and put it in place, and if not, then let the channel fetch the missing file, and restart itself.

As an alternative, they could be included in the plug-in in a sub directory, and copied in place during the first run.

But we have to at least also detect the CPU Arch. for this, if on Linux.

/T