11import argparse
22import sqlite3
3+ import sys
34
45from tabulate import tabulate
56
@@ -21,7 +22,7 @@ def str_range(stop: int) -> list:
2122 return [str (v ) for v in values ]
2223
2324
24- parser = argparse .ArgumentParser (' Transfer Plex user viewstate between users' )
25+ parser = argparse .ArgumentParser (" Transfer Plex user viewstate between users" )
2526parser .add_argument ("-p" , "--db-path" , help = "Path to com.plexapp.plugins.library.db file" )
2627args = parser .parse_args ()
2728
@@ -45,34 +46,75 @@ def str_range(stop: int) -> list:
4546))
4647
4748# Ask user to select target user
48- targetUserIndex = int (get_valid_input (
49+ targetAccountIndex = int (get_valid_input (
4950 "Now, please enter the _target_ account index" ,
5051 str_range (len (accounts )),
5152 "No account with that ID, please enter a valid account index"
5253))
5354
5455# Ask user to select copying/moving view state
5556mode = get_valid_input (
56- "Do you want to copy or move the viewstate? (copy/ move)" ,
57+ "Do you want to copy or move the viewstate? ([c]opy/[m] move)" ,
5758 ["c" , "copy" , "m" , "move" ],
58- "Please select either (c) opy or (m) ove"
59+ "Please select either [c] opy or [m] ove"
5960)
6061
6162# Get source account viewstate
6263sql = "SELECT * FROM metadata_item_settings WHERE account_id = :account_id"
63- cursor .execute (sql , {"account_id" : accounts [sourceAccountIndex ]['id' ]})
64+ cursor .execute (sql , {"account_id" : accounts [sourceAccountIndex ]["id" ]})
6465sourceViewstate = cursor .fetchall ()
6566
67+ # Make sure source account has any viewstate items
68+ if len (sourceViewstate ) == 0 :
69+ sys .exit ("Source account has no viewstate items" )
70+
71+ # Check for existing viewstate items of target user
72+ sql = "SELECT id, guid FROM metadata_item_settings WHERE account_id = :account_id"
73+ cursor .execute (sql , {"account_id" : accounts [targetAccountIndex ]['id' ]})
74+ targetViewstate = cursor .fetchall ()
75+
76+ # Handle existing target account viewstates items if any were found
77+ if len (targetViewstate ) > 0 :
78+ print (f"Found { len (targetViewstate )} existing viewstate items for target account" )
79+ handleExisting = get_valid_input (
80+ "Do you want to add to or replace existing viewstate? ([a]dd/[r]eplace)" ,
81+ ["a" , "add" , "r" , "replace" ],
82+ "Please select either [a]dd/[r]eplace"
83+ )
84+
85+ if handleExisting in ["a" , "add" ]:
86+ # Remove source account's viewstate for any media items for which target user has an existing viewstate
87+ sourceViewstateGuids = [e ["guid" ] for e in sourceViewstate ]
88+ targetViewstateGuids = [e ["guid" ] for e in targetViewstate ]
89+ for guid in targetViewstateGuids :
90+ # Check if guid is also present in source viewstate
91+ if guid in sourceViewstateGuids :
92+ # Get index of media item
93+ # (indexes are the same between guid list and overall source viewstate list,
94+ # so we can use the index to remove the media item's source viewstate from the copy/move list)
95+ index = sourceViewstateGuids .index (guid )
96+ # Remove viewstate for media item from copy/move list
97+ del sourceViewstate [index ], sourceViewstateGuids [index ]
98+
99+ # If we are moving the viewstate, remove viewstate items we don't need to move from source user
100+ sql = "DELETE FROM metadata_item_settings WHERE account_id = :account_id AND guid = :guid"
101+ cursor .execute (sql , {"account_id" : accounts [sourceAccountIndex ]["id" ], "guid" : guid })
102+ elif handleExisting in ["r" , "replace" ]:
103+ # Remove existing viewstate items
104+ for entry in targetViewstate :
105+ sql = "DELETE FROM metadata_item_settings WHERE id = :id"
106+ cursor .execute (sql , {"id" : entry ["id" ]})
107+
66108if mode in ["c" , "copy" ]:
67109 print (f"Copying viewstate ({ len (sourceViewstate )} items) "
68- f"from { accounts [sourceAccountIndex ]['name' ]} to { accounts [targetUserIndex ]['name' ]} " )
110+ f"from { accounts [sourceAccountIndex ]['name' ]} to { accounts [targetAccountIndex ]['name' ]} " )
69111
70112 for entry in sourceViewstate :
71113 sql = "INSERT INTO metadata_item_settings (account_id, guid, rating, view_offset, view_count, last_viewed_at, " \
72114 "created_at, updated_at, skip_count, last_skipped_at, changed_at, extra_data, last_rated_at) VALUES (?, " \
73115 "?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) "
74116 cursor .execute (sql , (
75- accounts [targetUserIndex ]["id" ],
117+ accounts [targetAccountIndex ]["id" ],
76118 entry ["guid" ],
77119 entry ["rating" ],
78120 entry ["view_offset" ],
@@ -90,11 +132,11 @@ def str_range(stop: int) -> list:
90132 connection .commit ()
91133elif mode in ["m" , "move" ]:
92134 print (f"Moving viewstate ({ len (sourceViewstate )} items) "
93- f"from { accounts [sourceAccountIndex ]['name' ]} to { accounts [targetUserIndex ]['name' ]} " )
135+ f"from { accounts [sourceAccountIndex ]['name' ]} to { accounts [targetAccountIndex ]['name' ]} " )
94136
95137 sql = "UPDATE metadata_item_settings SET account_id = :target_account_id WHERE account_id = :source_account_id"
96138 cursor .execute (sql , {
97- "target_account_id" : accounts [targetUserIndex ]["id" ],
139+ "target_account_id" : accounts [targetAccountIndex ]["id" ],
98140 "source_account_id" : accounts [sourceAccountIndex ]["id" ]
99141 })
100142
0 commit comments