Skip to content

Commit 5323c7c

Browse files
committed
Added support for handling existing target account viewstate
Existing target account's viewstate can be added to or replaced
1 parent 710979f commit 5323c7c

File tree

2 files changed

+51
-10
lines changed

2 files changed

+51
-10
lines changed

README.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,5 +42,4 @@ If you have Python >=3.6 installed on your system, you can use the Python script
4242

4343
## Known limitations
4444
- users/accounts only appear in database after connecting to the server and/or playing a media item from it at least once
45-
- script currently does not check whether target user already has viewstate information for a library item (may result in two conflicting viewstates)
4645
- Tautulli ([A Python based monitoring and tracking tool for Plex Media Server](https://tautulli.com)) will not pick up any of the transferred viewstate information

transfer-plex-user-viewstate.py

Lines changed: 51 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import argparse
22
import sqlite3
3+
import sys
34

45
from 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")
2526
parser.add_argument("-p", "--db-path", help="Path to com.plexapp.plugins.library.db file")
2627
args = 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
5556
mode = 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
6263
sql = "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"]})
6465
sourceViewstate = 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+
66108
if 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()
91133
elif 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

Comments
 (0)