Casvt / Plex-scripts

Plex, the arr's and tautulli scripts coming from user requests
GNU General Public License v3.0
341 stars 30 forks source link

Export and import user data in un-linked way #65

Closed RBeatse closed 2 years ago

RBeatse commented 2 years ago

I have a couple of managed users that are now old enough to have their own account. If I have them create a new account, is there a way to have a script copy all of their watched information (and anything else that is important) to their new "regular" account?

Casvt commented 2 years ago

Very interesting. This is doable yeah (though it can turn out to be a script of bigger size due to all the user settings and configurations and preferences and watch status etc. etc.).

You can basically make a more abstract description of this request: "Exporting/Importing user specific info where you can select the user". So you export selecting a managed user. And then import with the regular account selected. This way you're transferring user settings/data to the new account.

The request is doable. The real question is: what to transfer? I'm thinking of watch status and playlists. But there are also account settings (e.g. preferred subtitle language), tracks that are selected (e.g. the audio track for a movie), that new watchlist feature. The list goes on and on, but I like the idea.

RBeatse commented 2 years ago

For me, the basics is fine but I'm sure others might want the super details. I agree that the best idea would be to allow for the export of a user data and then allow for the import back. My only concern is if there is anything that would keep that "Managed" data from being imported into a "Regular" user. If not, we are good to go!

Casvt commented 2 years ago

Exporting from managed and importing to regular shouldn't be a problem. The way I grab user specific info doesn't look at the account type.

Casvt commented 2 years ago

Hey just had a thought: why not make it a feature of plex_exporter_importer? That script basically has everything setup already (aside from very specific things like settings/prefs and stream selection but that's not a problem). So I just add an argument that lets you only export user specific data and done. Sound's way easier than re-inventing the wheel.

RBeatse commented 2 years ago

That would work. For this particular use case, you would have to be able to export a particular user (or after you export all users), you would need to be able to specify that one user info and the ID of the user you want it to import into in case there is any name changes between managed and regular user.

Casvt commented 2 years ago

Selecting the user to export from is already done in the script: you can select which users to export data from. What I didn't think of is that for your use case, the usernames will change. The script grabs the info of a user and puts it together under the id of the user. Then when importing, we search for the username and if it's id is in the file, we import it's data. But that means that if you want to import user data to a different user, that wouldn't work. So I'd have to make some hybrid system with two "selection" options:

  1. You give the user name and it exports it's data; when importing it searches for the users id and if present imports the corresponding data; this is how it currently works in the script
  2. OR you select user data from the file and "link" it to a username, after which the script will import the user data to that user regardless of if that user was also the source of the data (your use case)

It's doable... but definitely a challenge. I like it :)

RBeatse commented 2 years ago

Also, where does this user data live when it is exported? All of the media data lives with the media so does the user data live in the same folder as the python script? Could you specify where to put it?

Casvt commented 2 years ago

Yeah I haven't decided yet. Currently, user data is stored for every user for every media (inside the .json file along side the media file, there is an entry for every selected user containing the user data about the media like watched status). However, user data like playlists doesn't really have a "location" in the plex file system so I haven't decided yet where to put the file containing such info. Any ideas are welcome.

RBeatse commented 2 years ago

I think giving an argument that lets the user choose may be the best way because unless you place it where the script itself lives, there isn't anywhere "normal to place it.
Personally, I would want to set it to a location that I backup regularly.

Casvt commented 2 years ago

Yeah I think that's the way to go

Casvt commented 2 years ago

I'm working on the script right now. Just to let you know, it's going to work very similarly to plex_exporter_importer.py. You have a database file with everything stored inside. See example usage below:

#Export the user data of the user PLEX_USERNAME and save it in the database under the name DATABASE_NAME
python3 user_exporter_importer.py -t "export" -p "watched_status" -p "playlist" -u "PLEX_USERNAME" -n "DATABASE_NAME"

#Get a list of all saved names (user data saved under name) in the database (aka the user data entries that are available)
python3 user_exporter_importer.py -t "list"

#Import the user data saved in the database under the name DATABASE_NAME and apply it to the plex user with the username PLEX_USERNAME
python3 user_exporter_importer.py -t "import" -p "watched_status" -p "playlist" -u "PLEX_USERNAME" -n "DATABASE_NAME"

You can see by the example that the user data is not linked to an id or username but instead to a chosen name. This makes it possible to apply user data to an other user than that it came from. See flow chart below:

CHILD_1_PLEX_ACCOUNT ((managed) plex user)
    |
    |
 export
    |
    v
CHILD1 (database name) 
    |
    |
 import
    |
    v
CHILD_1_NEW_PLEX_ACCOUNT ((regular) plex user)

That's the plan at least. Let me know if you have a better idea haha, but I think this is the best way to achieve what you want \:)

RBeatse commented 2 years ago

This seems the logical way to me

Casvt commented 2 years ago

Request completed. You can find the script here: https://github.com/Casvt/Plex-scripts/blob/main/changing_settings/user_exporter_importer.py.

Enjoy!

Casvt commented 2 years ago

Script has been updated. Importing turned out to be incomplete: not all exported data was being imported. That's fixed now.

RBeatse commented 2 years ago

Awesome. I'm currently moving so won't be able to look at it for a little bit...kind of crazy right now.

Casvt commented 2 years ago

Oh no problem. I just make it and after that I don't really care who or when somebody uses it. Take your time :) And enjoy your new home!

P.S. Playlist support has been added to plex_exporter_importer.py. See that issue

RBeatse commented 1 year ago

Casvt, it has been a long time! We are finally in our new house and, while moving, my main pc where Plex runs...died. Had to do a new motherboard and C drive. That meant I had to reinstall all of my apps but I had all of my "data". So, a lot of my posters no longer exist and the collections don't have their stuff either but I have my backup!! I have installed Python and copied my previous bat file and changed "export" to "import" (I also changed my up, port, and token to the new pc) but I get a message saying that Python isn't installed. I know it will be something easy but reinstalling everything has been a bit overwhelming!! Thanks

Casvt commented 1 year ago

No problem. As long as you have the DB file from exporting, it should be as easy as importing with the database file selected and everything comes back to life :)

#plex_exporter_importer.py
python3 plex_exporter_importer.py -L plex_exporter_importer.db --All -p metadata -p watched_status -p poster -p art -p collection -p playlist
RBeatse commented 1 year ago

That is what my batch file looks like but it tells me I don't have python installed when I just downloaded it from the website and had it installed and put into the PATH and such. I feel like there is a step I am missing (it said it was installing pip also). Is there a command I can run to make sure everything is setup correctly?

Casvt commented 1 year ago

Well I don't know what you mean by batch file because I've never written a batch file in my life. Open a terminal and just run python3. Does that work? You can simply install python from the Microsoft store.

When you have python working, run python3 -m pip install requests. Then run the script.

RBeatse commented 1 year ago

The bat file is mine that has the commands in it so I can easily schedule it. I'll try your commands. Thanks

RBeatse commented 1 year ago

I did all of that (thank you). The command I am running is

python3 plex_exporter_importer.py -t import -L plex_exporter_importer.db --All -p metadata -p poster -p art -p intro_marker -p collection

I get this in the log file Importing from plex_exporter_importer.db You're going to import the following: The standard plex metadata like title, summary, tags and more. The (custom) poster of movies, shows, seasons, artists and albums. The (custom) art of movies, shows, seasons, artists and albums. The intro marker of episodes, which describes the beginning and end of the intro. The collections in every library This is going to be done for your server and your complete plex library.

Traceback (most recent call last): File "c:\Plex_util\plex_exporter_importer\plex_exporter_importer.py", line 1417, in response = plex_exporter_importer( File "c:\Plex_util\plex_exporter_importer\plex_exporter_importer.py", line 1059, in plex_exporter_importer from os import geteuid ImportError: cannot import name 'geteuid' from 'os' (C:\Program Files\WindowsApps\PythonSoftwareFoundation.Python.3.10_3.10.2288.0_x64__qbz5n2kfra8p0\lib\os.py)

Casvt commented 1 year ago

Seems like a bug with windows. For now you can run it without -p intro_marker.

RBeatse commented 1 year ago

Importing from plex_exporter_importer.db You're going to import the following: The standard plex metadata like title, summary, tags and more. The (custom) poster of movies, shows, seasons, artists and albums. The (custom) art of movies, shows, seasons, artists and albums. The collections in every library This is going to be done for your server and your complete plex library.

Collections Shutting down... Progress saved AN ERROR OCCURED. ALL YOUR PROGRESS IS SAVED. PLEASE SHARE THE FOLLOWING WITH THE DEVELOPER: Traceback (most recent call last): File "c:\Plex_util\plex_exporter_importer\plex_exporter_importer.py", line 1417, in response = plex_exporter_importer( File "c:\Plex_util\plex_exporter_importer\plex_exporter_importer.py", line 1354, in plex_exporter_importer _leave(exit_args, e=e) File "c:\Plex_util\plex_exporter_importer\plex_exporter_importer.py", line 442, in _leave raise e File "c:\Plex_util\plex_exporter_importer\plex_exporter_importer.py", line 1164, in plex_exporter_importer response = method(type='collection', data={}, watched_map=watched_map, timestamp_map=timestamp_map, media_lib_id=0, args) File "c:\Plex_util\plex_exporter_importer\plex_exporter_importer.py", line 727, in _import lib_content = dict(map(lambda m: (str(m['Guid']), m['ratingKey']), lib_output)) File "c:\Plex_util\plex_exporter_importer\plex_exporter_importer.py", line 727, in lib_content = dict(map(lambda m: (str(m['Guid']), m['ratingKey']), lib_output)) KeyError: 'Guid'

Casvt commented 1 year ago

I've made a bug request for the error at #164

RBeatse commented 1 year ago

Thanks

Casvt commented 1 year ago

Working on the error. One sec

Casvt commented 1 year ago

Alright please redownload the script and check if it works :)

Casvt commented 1 year ago

Fixed issue #164 too so if you redownload the script again you should also be able to import intro markers

RBeatse commented 1 year ago

Got the new code, ran it with -p intro_markers and got this error

usage: plex_exporter_importer.py [-h] -t {import,export,reset} -p {metadata,advanced_metadata,watched_status,poster,episode_poster,art,episode_art,collection,playlist,intro_marker,chapter_thumbnail,server_settings} [-L LOCATION] [-v] [-a] [--AllMovie] [--AllShow] [--AllMusic] [-l LIBRARYNAME] [-m MOVIENAME] [-s SERIESNAME] [-S SEASONNUMBER] [-e EPISODENUMBER] [-A ARTISTNAME] [-d ALBUMNAME] [-T TRACKNAME] plex_exporter_importer.py: error: Intro Marker- or Chapter Thumbnail importing or Chapter Thumbnail exporting is requested but script is not run as root Importing from plex_exporter_importer.db You're going to import the following: The standard plex metadata like title, summary, tags and more. The (custom) poster of movies, shows, seasons, artists and albums. The (custom) art of movies, shows, seasons, artists and albums. The intro marker of episodes, which describes the beginning and end of the intro. The collections in every library This is going to be done for your server and your complete plex library.

Time: 0.012s

So, I just removed it but then got this error

Importing from plex_exporter_importer.db You're going to import the following: The standard plex metadata like title, summary, tags and more. The (custom) poster of movies, shows, seasons, artists and albums. The (custom) art of movies, shows, seasons, artists and albums. The collections in every library This is going to be done for your server and your complete plex library.

Collections Shutting down... Progress saved AN ERROR OCCURED. ALL YOUR PROGRESS IS SAVED. PLEASE SHARE THE FOLLOWING WITH THE DEVELOPER: Traceback (most recent call last): File "c:\Plex_util\plex_exporter_importer\plex_exporter_importer.py", line 1423, in response = plex_exporter_importer( File "c:\Plex_util\plex_exporter_importer\plex_exporter_importer.py", line 1360, in plex_exporter_importer _leave(exit_args, e=e) File "c:\Plex_util\plex_exporter_importer\plex_exporter_importer.py", line 443, in _leave raise e File "c:\Plex_util\plex_exporter_importer\plex_exporter_importer.py", line 1170, in plex_exporter_importer response = method(type='collection', data={}, watched_map=watched_map, timestamp_map=timestamp_map, media_lib_id=0, args) File "c:\Plex_util\plex_exporter_importer\plex_exporter_importer.py", line 728, in _import lib_content = {m['Guid']: m['ratingKey'] for m in lib_output if 'Guid' in m} File "c:\Plex_util\plex_exporter_importer\plex_exporter_importer.py", line 728, in lib_content = {m['Guid']: m['ratingKey'] for m in lib_output if 'Guid' in m} TypeError: unhashable type: 'list'

Casvt commented 1 year ago

First error:

It says what's wrong right in the error message: Intro Marker- or Chapter Thumbnail importing or Chapter Thumbnail exporting is requested but script is not run as root. When importing intro markers, the script should be run in a terminal that was launched with administrator privileges.

Second error:

A little mistake I made. I'll fix it when I'm done eating in +- half an hour. You can leave collections out of the command if you can't wait the 30min :)

Casvt commented 1 year ago

Fixed the second error

RBeatse commented 1 year ago

Importing from plex_exporter_importer.db You're going to import the following: The standard plex metadata like title, summary, tags and more. The (custom) poster of movies, shows, seasons, artists and albums. The (custom) art of movies, shows, seasons, artists and albums. The collections in every library This is going to be done for your server and your complete plex library.

Collections Shutting down... Progress saved AN ERROR OCCURED. ALL YOUR PROGRESS IS SAVED. PLEASE SHARE THE FOLLOWING WITH THE DEVELOPER: Traceback (most recent call last): File "c:\Plex_util\plex_exporter_importer\plex_exporter_importer.py", line 1423, in response = plex_exporter_importer( File "c:\Plex_util\plex_exporter_importer\plex_exporter_importer.py", line 1360, in plex_exporter_importer _leave(exit_args, e=e) File "c:\Plex_util\plex_exporter_importer\plex_exporter_importer.py", line 443, in _leave raise e File "c:\Plex_util\plex_exporter_importer\plex_exporter_importer.py", line 1170, in plex_exporter_importer response = method(type='collection', data={}, watched_map=watched_map, timestamp_map=timestamp_map, media_lib_id=0, args) File "c:\Plex_util\plex_exporter_importer\plex_exporter_importer.py", line 743, in _import new_ratingkey = ssn.post(f'{base_url}/library/collections', params={'title': collection[2], 'smart': '0', 'sectionId': lib['key'], 'type': media_types[lib['type']][3], 'uri': f'server://{machine_id}/com.plexapp.plugins.library/library/metadata/{",".join(collection_keys)}'}).json()['MediaContainer']['Metadata'][0]['ratingKey'] TypeError: sequence item 1: expected str instance, NoneType found

RBeatse commented 1 year ago

I opened a bug rather than keeping commenting on this closed item.