Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -209,3 +209,9 @@ cython_debug/
marimo/_static/
marimo/_lsp/
__marimo__/

# VSCode
.vscode

# Metadata
metadata
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,14 @@ Metadata is sourced from [one-pace-metadata](https://github.com/ladyisatis/one-p

Contributions via Pull Requests are always welcome if I miss a bug or want another feature added in, want to contribute a program icon, etc.

## Running from source

Details are available [on the wiki](https://github.com/ladyisatis/OnePaceOrganizer/wiki/Frequently-Asked-Questions#how-do-i-run-this-from-source), but in short you can run this from source by running:

```bash
uv run main.py gui
```

## Thanks

- Craigy (@verywittyname on Discord) for Episode Descriptions spreadsheets and updates
Expand Down
4 changes: 4 additions & 0 deletions main.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ def main():
parser.add_argument("--plex-library", help="Plex Library Key", default=None)
parser.add_argument("--plex-show", help="Plex Show GUID", default=None)
parser.add_argument("--plex-set-show-edits", help="Overwrite Plex show information", default=None, type=strbool)
parser.add_argument("--plex-scan-show-folder", help="Trigger Plex Library scan after moving/copying", default=None, type=strbool)
parser.add_argument("--plex-code", help="Plex 2-Factor Auth Code (headless mode only)", default=None)
parser.add_argument("--plex-remember", help="Remember Plex Credentials", default=None, type=strbool)
parser.add_argument("--plex-retry-times", help="How many times Organizer should retry to fetch a Plex season/episode", default=None)
Expand Down Expand Up @@ -145,6 +146,9 @@ def main():
if args.plex_set_show_edits is not None:
opo.plex_set_show_edits = args.plex_set_show_edits

if args.plex_scan_show_folder is not None:
opo.plex_scan_show_folder = args.plex_scan_show_folder

if args.plex_retry_times is not None:
opo.plex_retry_times = int(args.plex_retry_times)

Expand Down
9 changes: 9 additions & 0 deletions src/console.py
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,15 @@ async def run(self):
]
).run_async()

self.organizer.plex_scan_show_folder = await button_dialog(
title=self.window_title,
text="Do you want to trigger Plex to Scan the Library files after import or there's no need? (eg if Plex settings do that automatically already)",
buttons=[
("Tell Plex to Scan", True),
("No need", False)
]
).run_async()

if plex_config_enabled:
success = await self.run_plex_wizard()
if not success:
Expand Down
53 changes: 32 additions & 21 deletions src/gui.py
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,13 @@ def __init__(self, organizer=None, log_level="info"):
self.action_edit_plex_retry_secs.triggered.connect(self.edit_plex_retry_secs)
menu_advanced.addAction(self.action_edit_plex_retry_secs)

self.action_scan_show_folder = QAction("(Plex) Scan Library after move/copy", self)
self.action_scan_show_folder.setCheckable(True)
self.action_scan_show_folder.setChecked(self.organizer.plex_scan_show_folder)
self.action_scan_show_folder.setVisible(self.organizer.mode != 0)
self.action_scan_show_folder.triggered.connect(self.scan_show_folder)
menu_advanced.addAction(self.action_scan_show_folder)

action_edit_workers = QAction("Edit Number of Workers", self)
action_edit_workers.triggered.connect(self.edit_workers)
menu_advanced.addAction(action_edit_workers)
Expand Down Expand Up @@ -620,6 +627,7 @@ def set_method(self, mode):
self.action_edit_plex_retry_secs.setVisible(plex_enabled)
self.action_overwrite_nfo.setVisible(not plex_enabled)
self.action_set_show.setVisible(plex_enabled)
self.action_scan_show_folder.setVisible(plex_enabled)
self.output.setVisible(self.organizer.file_action != 4)
self.input.label.setText(self.input_metadata_str if self.organizer.file_action == 4 else self.input_nometadata_str)
self.input.setVisible(self.organizer.file_action != 4 if plex_enabled else True)
Expand Down Expand Up @@ -1138,10 +1146,12 @@ async def select_plex_show(self):
self.plex_status.prop.setText(self.plex_status_text())

@asyncClose
async def exit(self, event=None):
async def closeEvent(self, event):
await self.organizer.save_config()
if event is None:
self.close()
event.accept()

def exit(self):
self.close() # delegates to closeEvent above

def set_action(self, action):
self.organizer.file_action = action
Expand Down Expand Up @@ -1225,6 +1235,10 @@ def set_show_edits(self):
self.organizer.plex_set_show_edits = not self.organizer.plex_set_show_edits
self.action_set_show.setChecked(self.organizer.plex_set_show_edits)

def scan_show_folder(self):
self.organizer.plex_scan_show_folder = not self.organizer.plex_scan_show_folder
self.action_scan_show_folder.setChecked(self.organizer.plex_scan_show_folder)

@asyncSlot()
async def start(self):
self.start_button.setEnabled(False)
Expand Down Expand Up @@ -1343,44 +1357,41 @@ async def start(self):
def main(organizer, log_level):
try:
app = QApplication(sys.argv)
close_event = asyncio.Event()
app.aboutToQuit.connect(close_event.set)

loop = QEventLoop(app)
asyncio.set_event_loop(loop)

async def _run():
await organizer.load_config()

gui = GUI(organizer, log_level)
gui.setWindowTitle(organizer.window_title)

if "gui_maximized" in organizer.extra_fields and isinstance(organizer.extra_fields["gui_maximized"], bool) and organizer.extra_fields["gui_maximized"]:
if organizer.extra_fields.get("gui_maximized") is True:
gui.showMaximized()
else:
gui.show()

is_latest, latest_vers = await utils.run(utils.is_up_to_date, organizer.toml["version"], organizer.base_path)
if not is_latest:
do_update = await asyncWrap(
QMessageBox.question(
lambda: QMessageBox.question(
None,
self.organizer.window_title,
"There is a newer version of this application. Do you wish to open up " +
"GitHub in order to download the new version?" +
"\n\n" +
f"Installed: v{organizer.toml['version']}\n" +
organizer.window_title,
"There is a newer version of this application. Do you wish to open up "
"GitHub in order to download the new version?"
"\n\n"
f"Installed: v{organizer.toml['version']}\n"
f"Latest: v{latest_vers}"
) == QMessageBox.StandardButton.Yes
)

if do_update:
await utils.run(webbrowser.open_new_tab, "https://github.com/ladyisatis/OnePaceOrganizer/releases/latest")

await close_event.wait()

asyncio.run(_run(), loop_factory=QEventLoop)
with loop:
loop.run_until_complete(_run())
loop.run_forever() # keeps running until app.quit() / window close exits the loop

except:
except Exception:
print(traceback.format_exc())
QMessageBox.critical(None, organizer.window_title, traceback.format_exc())

finally:
asyncio.run(organizer.save_config())
QMessageBox.critical(None, organizer.window_title, traceback.format_exc())
19 changes: 17 additions & 2 deletions src/organizer.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ def __init__(self):
self.plex_retry_secs = utils.get_env("plex_retry_secs", 30)
self.plex_retry_times = utils.get_env("plex_retry_times", 3)
self.plex_set_show_edits = utils.get_env("plex_set_show_edits", True)
self.plex_scan_show_folder = utils.get_env("plex_scan_show_folder", True)

self.progress_bar_func = None
self.message_dialog_func = None
Expand Down Expand Up @@ -228,6 +229,9 @@ async def load_config(self):
if "set_show_edits" in config["plex"] and config["plex"]["set_show_edits"] is not None:
self.plex_set_show_edits = config["plex"]["set_show_edits"]

if "scan_show_folder" in config["plex"] and config["plex"]["scan_show_folder"] is not None:
self.plex_scan_show_folder = config["plex"]["scan_show_folder"]

if "mode" in config and config["mode"] is not None and isinstance(config["mode"], int):
self.mode = config["mode"]

Expand Down Expand Up @@ -279,7 +283,8 @@ async def save_config(self):
},
"retry_secs": self.plex_retry_secs,
"retry_times": self.plex_retry_times,
"set_show_edits": self.plex_set_show_edits
"set_show_edits": self.plex_set_show_edits,
"scan_show_folder": self.plex_scan_show_folder
}
}

Expand Down Expand Up @@ -1085,7 +1090,7 @@ async def process_plex(self, files):
await utils.run(show.editOriginallyAvailable, str(tvshow["premiered"].isoformat()).split("T")[0])
else:
await utils.run(show.editOriginallyAvailable, tvshow["premiered"])

# Poster
src = await utils.run(utils.find_from_list, self.base_path, [
("posters", "poster.*"),
Expand Down Expand Up @@ -1236,8 +1241,18 @@ async def process_plex_episodes(self, queue, metadata_only=False):
plex_episode = None

try:
data_file = Path(self.base_path, "metadata", "data.db")
if await utils.is_file(data_file):
await self.open_db(data_file)

section = await utils.run(self.plexapi_server.library.sectionByID, int(self.plex_config_library_key))

if self.plex_scan_show_folder:
self.logger.info(f"Triggering Plex Library Scan")
section.update()
self.logger.debug(f"Waiting 5 seconds for library update to finish")
await asyncio.sleep(5)

self.logger.trace(f"Looking up show with GUID: {self.plex_config_show_guid}")
if self.plex_config_show_guid.startswith("local://"):
rating_key = self.plex_config_show_guid.replace("local://", "")
Expand Down