Skip to content

Commit

Permalink
Add usage of X-Plex-Token in order to use the XML interface directly
Browse files Browse the repository at this point in the history
  • Loading branch information
StegSchreck committed Mar 23, 2019
1 parent 3f6e49a commit 98ea989
Show file tree
Hide file tree
Showing 7 changed files with 79 additions and 58 deletions.
23 changes: 20 additions & 3 deletions RatS.egg-info/PKG-INFO
Original file line number Diff line number Diff line change
Expand Up @@ -681,7 +681,7 @@ Description: <p align="center">
This project serves for analyzing, and transfering your ratings from one movie tracking / rating website to another.

The goal of this project is to have a universal tool which can transfer your ratings from any site to another without
the need of any manual steps like configuring an API access or whatever. Just configure you credentials (see steps
the need of any manual steps like configuring an API access or whatever. Just configure your credentials (see steps
below), start the tool and relax.

This also works if your lists are marked as private, as this tool uses a browser to login and get the content.
Expand All @@ -706,10 +706,27 @@ Description: <p align="center">
* Or execute `sudo ./InstallGeckodriver.sh`.
For this you will need to have tar, wget and curl installed.

1. Copy the `credentials.cfg.orig` file to `credentials.cfg` and insert your credentials for the sites you need there
1. Set your credentials

1. Copy the `credentials.cfg.orig` file to `credentials.cfg` and insert your credentials for the sites you need there
(without any quotation marks etc.).

Copying the file will conserve the possibility to do a `git pull` later on without overwriting your credentials.
Copying the file will conserve the possibility to do a `git pull` later on without overwriting your credentials.

1. Alternatively, you can set the credentials via environment variables. For example, if you want to set the
credentials for your IMDB and Trakt accounts you would need to set these environment variables:
```sh
export IMDB_USERNAME=abc@def.de
export IMDB_PASWORD=def
export TRAKT_USERNAME=abc
export TRAKT_PASWORD=def
```
It is important to use the variable names completely in uppercase!

1. The third way is to set the environment variables when running the transfer script, like this:
`IMDB_USERNAME=abc@def.de IMDB_PASWORD=def TRAKT_USERNAME=abc TRAKT_PASWORD=def python3 transfer_ratings.py --source trakt --destination imdb`

The credentials in environment variables are overruling the ones in the `credentials.cfg` file.
1. Execute the script with **Python3**
`python3 transfer_ratings.py --source trakt --destination movielens`

Expand Down
38 changes: 25 additions & 13 deletions RatS/plex/plex_ratings_inserter.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import math
import urllib.request

from bs4 import BeautifulSoup
Expand All @@ -15,9 +14,14 @@ def __init__(self, args):
super(PlexRatingsInserter, self).__init__(Plex(args), args)

def _search_for_movie(self, movie):
search_url = 'http://{base_url}/search?local=1&query={search_params}'.format(
base_url=self.site.BASE_URL, search_params=urllib.request.quote(movie['title'])
)
search_url = 'http://{base_url}/library/all?type=1&title={movie_title}' \
'&X-Plex-Container-Start=0' \
'&X-Plex-Container-Size=50' \
'&X-Plex-Token={plex_token}'.format(
base_url=self.site.BASE_URL,
movie_title=urllib.request.quote(movie['title']),
plex_token=self.site.PLEX_TOKEN
)

self.site.browser.get(search_url)

Expand All @@ -35,12 +39,13 @@ def _is_requested_movie(self, movie, search_result):

if is_requested_movie:
movie_id = search_result['ratingkey']
movie_url = 'http://{base_url}/web/index.html#!/server/{server_id}/details/{library_path}{movie_id}'.format(
base_url=self.site.BASE_URL,
server_id=self.site.SERVER_ID,
library_path='%2Flibrary%2Fmetadata%2F',
movie_id=movie_id
)
movie_url = 'http://{base_url}/web/index.html#!/server/{server_id}/details' \
'?key={library_path}{movie_id}'.format(
base_url=self.site.BASE_URL,
server_id=self.site.SERVER_ID,
library_path='%2Flibrary%2Fmetadata%2F',
movie_id=movie_id
)
self.site.browser.get(movie_url)
self._wait_for_movie_page_to_be_loaded()
return True
Expand All @@ -55,6 +60,13 @@ def _wait_for_movie_page_to_be_loaded(self):
)

def _click_rating(self, my_rating):
stars = self.site.browser.find_element_by_class_name('rating').find_elements_by_css_selector('span.star')
star_index = math.ceil(int(my_rating) / 2) - 1
stars[star_index].click()
movie_id = self.site.browser.current_url.split('%2Flibrary%2Fmetadata%2F')[-1]
rate_url = 'http://{base_url}/:/rate' \
'?key={movie_id}&identifier=com.plexapp.plugins.library' \
'&rating={my_rating}&X-Plex-Token={plex_token}'.format(
base_url=self.site.BASE_URL,
movie_id=movie_id,
my_rating=my_rating,
plex_token=self.site.PLEX_TOKEN
)
self.site.browser.get(rate_url)
14 changes: 8 additions & 6 deletions RatS/plex/plex_ratings_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,15 @@ def _get_movies_count(movie_ratings_page):

def _get_ratings_page(self, i):
page_size = 100
page_start = i * page_size
return 'http://{base_url}/library/sections/{section_id}/all' \
'?type=1&sort=rating:desc&X-Plex-Container-Start={page_start}&X-Plex-Container-Size={page_size}'.format(
page_start = (i - 1) * page_size
return 'http://{base_url}/library/all?type=1&userRating!=0' \
'&X-Plex-Container-Start={page_start}' \
'&X-Plex-Container-Size={page_size}' \
'&X-Plex-Token={plex_token}'.format(
base_url=self.site.BASE_URL,
section_id=self.site.MOVIE_SECTION_ID,
page_start=page_start,
page_size=page_size
page_size=page_size,
plex_token=self.site.PLEX_TOKEN
)

@staticmethod
Expand All @@ -46,7 +48,7 @@ def _parse_movie_tile(self, movie_tile):
movie[self.site.site_name.lower()]['my_rating'] = round(float(movie_tile['userrating']))
movie[self.site.site_name.lower()]['id'] = movie_tile['ratingkey']
movie[self.site.site_name.lower()]['url'] = \
'http://{base_url}/web/index.html#!/server/{server_id}/details/{library_path}{movie_id}'.format(
'http://{base_url}/web/index.html#!/server/{server_id}/details?key={library_path}{movie_id}'.format(
base_url=self.site.BASE_URL,
server_id=self.site.SERVER_ID,
library_path='%2Flibrary%2Fmetadata%2F',
Expand Down
35 changes: 19 additions & 16 deletions RatS/plex/plex_site.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import sys
import re
import time

from bs4 import BeautifulSoup
from selenium.webdriver.support import ui

from RatS.base.base_site import Site

Expand Down Expand Up @@ -29,28 +29,31 @@ def _user_is_not_logged_in(self):
return self.USERNAME not in self.browser.page_source

def _parse_configuration(self):
self.PLEX_TOKEN = self._determine_plex_token()
self.SERVER_ID = self._determine_server_id()
self.MY_RATINGS_URL = 'http://{base_url}/web/index.html#!/server/{server_id}/list' \
'?key=%2Fhubs%2Fhome%2FrecentlyAdded%3Ftype%3D1&type=movie'.format(
self.MY_RATINGS_URL = 'http://{base_url}/library/all?type=1&userRating!=0' \
'&X-Plex-Container-Start={page_start}' \
'&X-Plex-Container-Size={page_size}' \
'&X-Plex-Token={plex_token}'.format(
base_url=self.BASE_URL,
server_id=self.SERVER_ID
page_start=0,
page_size=100,
plex_token=self.PLEX_TOKEN
)

def _determine_movies_section_id(self):
sys.stdout.write('\r===== ' + self.site_displayname + ': determine movie section')
sys.stdout.flush()

def _determine_plex_token(self):
self.browser.get('http://{base_url}/web/index.html#'.format(base_url=self.BASE_URL))
time.sleep(1)
homepage = BeautifulSoup(self.browser.page_source, 'html.parser')
time.sleep(1)
wait = ui.WebDriverWait(self.browser, 600)
wait.until(lambda driver: driver.find_element_by_xpath("//button[@data-qa-id='metadataPosterMoreButton']"))

library_navigation_links = homepage.find('div', attrs={'data-qa-id': 'sidebarLibrariesList'})
movie_section = library_navigation_links.find('i', class_='plex-icon-movies-560').parent.parent.parent
self.browser.find_elements_by_xpath("//button[@data-qa-id='metadataPosterMoreButton']")[0].click()
self.browser.find_elements_by_xpath("//button[@role='menuitem']")[-1].click()
link_to_xml = self.browser.find_element_by_xpath("//div[@class='modal-footer']//a").get_attribute('href')
plex_token = re.findall(r'X-Plex-Token=(\w+)', link_to_xml)[0]

return movie_section['data-qa-id'].split('--')[-1]
return plex_token

def _determine_server_id(self):
self.browser.get('http://{base_url}/web/index.html#!/settings/server'.format(base_url=self.BASE_URL))
time.sleep(1)
time.sleep(2)
return self.browser.current_url.split('/')[-2]
5 changes: 3 additions & 2 deletions tests/unit/plex/test_plex_ratings_inserter.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,13 @@ def setUp(self):
self.search_result_tile_list = [result_tile.read()]

@patch('RatS.plex.plex_ratings_inserter.Plex._determine_server_id')
@patch('RatS.plex.plex_ratings_inserter.Plex._determine_movies_section_id')
@patch('RatS.plex.plex_ratings_inserter.Plex')
@patch('RatS.base.base_ratings_inserter.RatingsInserter.__init__')
@patch('RatS.utils.browser_handler.Firefox')
def test_init(self, browser_mock, base_init_mock, section_id_mock, server_id_mock):
def test_init(self, browser_mock, base_init_mock, site_mock, server_id_mock):
PlexRatingsInserter(None)

self.assertTrue(site_mock.called)
self.assertTrue(base_init_mock.called)

@patch('RatS.base.base_ratings_inserter.RatingsInserter._print_progress_bar')
Expand Down
7 changes: 4 additions & 3 deletions tests/unit/plex/test_plex_ratings_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,13 @@ def setUp(self):
self.ratings_tile = BeautifulSoup(ratings_tile.read(), 'html.parser').find('video', attrs={'type': 'movie'})

@patch('RatS.plex.plex_ratings_inserter.Plex._determine_server_id')
@patch('RatS.plex.plex_ratings_inserter.Plex._determine_movies_section_id')
@patch('RatS.plex.plex_ratings_parser.Plex')
@patch('RatS.base.base_ratings_parser.RatingsParser.__init__')
@patch('RatS.utils.browser_handler.Firefox')
def test_init(self, browser_mock, base_init_mock, section_id_mock, server_id_mock):
def test_init(self, browser_mock, base_init_mock, site_mock, server_id_mock):
PlexRatingsParser(None)

self.assertTrue(site_mock.called)
self.assertTrue(base_init_mock.called)

@patch('RatS.plex.plex_ratings_parser.PlexRatingsParser._print_progress_bar')
Expand Down Expand Up @@ -69,4 +70,4 @@ def test_parser_single_movie(self, site_mock, base_init_mock, browser_mock):
self.assertEqual('19542', movie['plex']['id'])
self.assertEqual('http://localhost:12345/web/index.html#!'
'/server/ThisIsAMockUUID/'
'details/%2Flibrary%2Fmetadata%2F19542', movie['plex']['url'])
'details?key=%2Flibrary%2Fmetadata%2F19542', movie['plex']['url'])
15 changes: 0 additions & 15 deletions tests/unit/plex/test_plex_site.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,6 @@ class PlexSiteTest(TestCase):
def setUp(self):
if not os.path.exists(os.path.join(TESTDATA_PATH, 'exports')):
os.makedirs(os.path.join(TESTDATA_PATH, 'exports'))
with open(os.path.join(TESTDATA_PATH, 'plex', 'sections.xml'), encoding='UTF-8') as server_sections:
self.server_sections = server_sections.read()

@patch('RatS.plex.plex_site.Plex._parse_configuration')
@patch('RatS.utils.browser_handler.Firefox')
@patch('RatS.base.base_site.Site._init_browser')
def test_determine_movie_section_id(self, init_browser_mock, browser_mock, configuration_mock):
browser_mock.page_source = self.server_sections
site = Plex(None)
site.browser = browser_mock
site.BASE_URL = 'localhost'

result = site._determine_movies_section_id() # pylint: disable=protected-access

self.assertEqual('5', result)

@patch('RatS.plex.plex_site.Plex._parse_configuration')
@patch('RatS.utils.browser_handler.Firefox')
Expand Down

0 comments on commit 98ea989

Please sign in to comment.