Skip to content

Commit

Permalink
Rewrite slow.pics session handling (#210)
Browse files Browse the repository at this point in the history
You no longer need to get cookies from your browser. I will now create its own session by using the user provided credentials. It only stores the cookies, so no password or usernames are stored on disk.
  • Loading branch information
Kapppa authored Oct 21, 2024
1 parent 5c385e9 commit eb6d560
Show file tree
Hide file tree
Showing 4 changed files with 72 additions and 45 deletions.
2 changes: 1 addition & 1 deletion vspreview/plugins/builtins/slowpics_comp/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -664,7 +664,7 @@ def get_slowpics_conf(self, uuid: str, samples: list[Frame]) -> WorkerConfigurat
self.is_public_checkbox.isChecked(), self.is_nsfw_checkbox.isChecked(),
True, delete_after, sample_frames_int, self.settings.compression, path,
self.main, self.settings.delete_cache_enabled, self.settings.frame_type_enabled,
self.settings.browser_id, self.settings.session_id, tmdb_id, tags
self.settings.cookies_path, tmdb_id, tags
)

def find_samples(self, uuid: str) -> bool:
Expand Down
61 changes: 32 additions & 29 deletions vspreview/plugins/builtins/slowpics_comp/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,23 @@

from typing import Any

from stgpytools import SPath

from PyQt6.QtWidgets import QLabel

from vspreview.core import AbstractSettingsWidget, CheckBox, ComboBox, HBoxLayout, LineEdit, VBoxLayout, try_load
from vspreview.core.abstracts import PushButton
from vspreview.models import GeneralModel
from vspreview.plugins import AbstractPlugin
from .utils import do_login

__all__ = [
'CompSettings'
]


class CompSettings(AbstractSettingsWidget):
__slots__ = ('delete_cache_checkbox', 'frame_type_checkbox', 'login_browser_id_edit', 'login_session_edit', 'tmdb_apikey_edit')
__slots__ = ('delete_cache_checkbox', 'frame_type_checkbox', 'login_username_edit', 'login_password_edit', 'tmdb_apikey_edit')

DEFAULT_COLLECTION_NAME = 'Unknown'

Expand All @@ -23,6 +27,8 @@ def __init__(self, plugin: AbstractPlugin) -> None:

self.plugin = plugin

self.path = plugin.main.global_plugins_dir / 'slowpics_comp'

def setup_ui(self) -> None:
super().setup_ui()

Expand All @@ -35,8 +41,9 @@ def setup_ui(self) -> None:
self.default_public_checkbox = CheckBox('Default Public Flag')
self.default_nsfw_checkbox = CheckBox('Default NSFW Flag')

self.login_browser_id_edit = LineEdit('Browser ID')
self.login_session_edit = LineEdit('Session ID')
self.login_username_edit = LineEdit('Username')
self.login_password_edit = LineEdit('Password')
self.login_button = PushButton('Login', self, clicked=self.handle_login_click)

self.compression_combobox = ComboBox[str](model=GeneralModel[str](['fast', 'slow', 'uncompressed']))

Expand All @@ -45,15 +52,6 @@ def setup_ui(self) -> None:

self.tmdb_apikey_edit = LineEdit('API Key')

label = QLabel(
'To get this info: Open Dev console in browser, go to network tab, upload a comparison,'
'click request called "comparison" Copy browserId from payload, copy session token from '
'SLP-SESSION cookie from cookies'
)
label.setMaximumHeight(50)
label.setMinimumWidth(400)
label.setWordWrap(True)

VBoxLayout(self.vlayout, [
self.collection_name_template_edit,
QLabel('Available replaces: {tmdb_title}, {video_nodes}, {tmdb_year}')
Expand All @@ -67,7 +65,7 @@ def setup_ui(self) -> None:
]),
self.get_separator(),
VBoxLayout([
QLabel("Compression Type:"),
QLabel('Compression Type:'),
self.compression_combobox
])
]
Expand All @@ -81,10 +79,11 @@ def setup_ui(self) -> None:
HBoxLayout(
self.vlayout,
VBoxLayout([
HBoxLayout([QLabel("TMDB API Key"), self.tmdb_apikey_edit]),
label,
HBoxLayout([QLabel("Browser ID"), self.login_browser_id_edit]),
HBoxLayout([QLabel("Session ID"), self.login_session_edit]),
HBoxLayout([QLabel('TMDB API Key'), self.tmdb_apikey_edit]),
self.get_separator(),
HBoxLayout([QLabel('Username'), self.login_username_edit]),
HBoxLayout([QLabel('Password'), self.login_password_edit]),
self.login_button,
])
)

Expand All @@ -99,6 +98,22 @@ def set_defaults(self) -> None:
# https://github.com/Radarr/Radarr/blob/29ba6fe5563e737f0f87919e48f556e39284e6bb/src/NzbDrone.Common/Cloud/RadarrCloudRequestBuilder.cs#L31
self.tmdb_apikey_edit.setText('eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJhdWQiOiIxYTczNzMzMDE5NjFkMDNmOTdmODUzYTg3NmRkMTIxMiIsInN1YiI6IjU4NjRmNTkyYzNhMzY4MGFiNjAxNzUzNCIsInNjb3BlcyI6WyJhcGlfcmVhZCJdLCJ2ZXJzaW9uIjoxfQ.gh1BwogCCKOda6xj9FRMgAAj_RYKMMPC3oNlcBtlmwk') # noqa

def handle_login_click(self) -> None:
username = self.login_username_edit.text()
password = self.login_password_edit.text()

if not username or not password:
return

do_login(username, password, self.cookies_path)

self.login_username_edit.setText('')
self.login_password_edit.setText('')

@property
def cookies_path(self) -> SPath:
return self.path / 'cookies.json'

@property
def delete_cache_enabled(self) -> bool:
return self.delete_cache_checkbox.isChecked()
Expand All @@ -107,14 +122,6 @@ def delete_cache_enabled(self) -> bool:
def frame_type_enabled(self) -> bool:
return self.frame_type_checkbox.isChecked()

@property
def browser_id(self) -> str:
return self.login_browser_id_edit.text()

@property
def session_id(self) -> str:
return self.login_session_edit.text()

@property
def tmdb_apikey(self) -> str:
return self.tmdb_apikey_edit.text()
Expand Down Expand Up @@ -143,8 +150,6 @@ def __getstate__(self) -> dict[str, Any]:
return {
'delete_cache_enabled': self.delete_cache_enabled,
'frame_type_enabled': self.frame_type_enabled,
'browser_id': self.browser_id,
'session_id': self.session_id,
'tmdb_apikey': self.tmdb_apikey,
'compression': self.compression,
'default_public': self.default_public,
Expand All @@ -159,8 +164,6 @@ def __setstate__(self, state: dict[str, Any]) -> None:
try_load(state, 'default_public', bool, self.default_public_checkbox.setChecked)
try_load(state, 'default_nsfw', bool, self.default_nsfw_checkbox.setChecked)
try_load(state, 'collection_name_template', str, self.collection_name_template_edit.setText)
try_load(state, 'browser_id', str, self.login_browser_id_edit.setText)
try_load(state, 'session_id', str, self.login_session_edit.setText)
try_load(state, 'tmdb_apikey', str, self.tmdb_apikey_edit.setText)
try_load(state, 'compression', int, self.compression_combobox.setCurrentIndex)
try_load(state, 'frame_ntype', str, self.frame_ntype_combobox.setCurrentValue)
30 changes: 30 additions & 0 deletions vspreview/plugins/builtins/slowpics_comp/utils.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
from __future__ import annotations

import json
import logging
import re
import unicodedata
from typing import Callable, Final
from uuid import uuid4

from requests import HTTPError, Session
from requests.utils import dict_from_cookiejar
from requests_toolbelt import MultipartEncoder # type: ignore
from vstools import SPath

Expand Down Expand Up @@ -145,3 +147,31 @@ def get_frame_time(main: MainWindow, output: VideoOutput, frame: int, max_value:
return time_str

return f'{time_str} / {frame_str}'


def do_login(username:str, password:str, path:SPath):
path.parent.mkdir(parents=True, exist_ok=True)

with Session() as session:
session.headers.update(get_slowpic_headers(session))

home = session.get('https://slow.pics/login')
home.raise_for_status()

csrf = re.search(r'<input type="hidden" name="_csrf" value="([a-zA-Z0-9-_]+)"\/>', home.text)

if not csrf:
raise Exception('Couldn\'t find csrf')

login_params = {
"_csrf": csrf.group(1),
"username": username,
"password": password,
"remember-me": 'on'
}


login = session.post('https://slow.pics/login', data=login_params, allow_redirects=True)
login.raise_for_status()

path.write_text(json.dumps(dict_from_cookiejar(session.cookies)))
24 changes: 9 additions & 15 deletions vspreview/plugins/builtins/slowpics_comp/workers.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from __future__ import annotations

import json
import logging
import random
import shutil
Expand All @@ -12,6 +13,7 @@
from requests import Session
from requests_toolbelt import MultipartEncoder # type: ignore
from requests_toolbelt import MultipartEncoderMonitor
from requests.utils import cookiejar_from_dict
from stgpytools import SPath, ndigits
from vstools import clip_data_gather, get_prop, remap_frames, vs

Expand Down Expand Up @@ -44,8 +46,7 @@ class WorkerConfiguration(NamedTuple):
main: MainWindow
delete_cache: bool
frame_type: bool
browser_id: str
session_id: str
cookies: SPath
tmdb: str
tags: list[str]

Expand Down Expand Up @@ -73,10 +74,9 @@ def run(self, conf: WorkerConfiguration) -> None:
all_image_types = list[list[str]]()
conf.path.mkdir(parents=True, exist_ok=False)

if conf.browser_id and conf.session_id:
if conf.cookies.is_file():
with Session() as sess:
sess.cookies.set('SLP-SESSION', conf.session_id, domain='slow.pics')
browser_id = conf.browser_id
sess.cookies.update(cookiejar_from_dict(json.loads(conf.cookies.read_text())))
base_page = sess.get('https://slow.pics/comparison')
if base_page.text.find('id="logoutBtn"') == -1:
self.progress_status.emit(conf.uuid, 'Session Expired', 0, 0)
Expand All @@ -92,9 +92,6 @@ def run(self, conf: WorkerConfiguration) -> None:
path_name = conf.path / folder_name
path_name.mkdir(parents=True)

if not conf.frames[i]:
raise StopIteration("Output missing a frame.")

curr_filename = (path_name / folder_name).append_to_stem(f'%0{ndigits(max(conf.frames[i]))}d').with_suffix('.png')

clip = output.prepare_vs_output(
Expand Down Expand Up @@ -164,13 +161,10 @@ def frame_callback(n: int, f: vs.VideoFrame) -> str:
self.progress_status.emit(conf.uuid, 'upload', 0, 0)

with Session() as sess:
if conf.browser_id and conf.session_id:
sess.cookies.set('SLP-SESSION', conf.session_id, domain='slow.pics')
browser_id = conf.browser_id
check_session = True
else:
browser_id = str(uuid4())
check_session = False
browser_id = str(uuid4())
check_session = conf.cookies.is_file()
if check_session:
sess.cookies.update(cookiejar_from_dict(json.loads(conf.cookies.read_text())))

base_page = sess.get('https://slow.pics/comparison', headers=get_slowpic_headers(sess))
if self.isFinished():
Expand Down

0 comments on commit eb6d560

Please sign in to comment.