Skip to content

Commit

Permalink
Merge 98ea989 into 039a90e
Browse files Browse the repository at this point in the history
  • Loading branch information
StegSchreck committed Mar 23, 2019
2 parents 039a90e + 98ea989 commit fd1e4ff
Show file tree
Hide file tree
Showing 7 changed files with 92 additions and 59 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
49 changes: 32 additions & 17 deletions RatS/plex/plex_site.py
Original file line number Diff line number Diff line change
@@ -1,44 +1,59 @@
import sys
import re
import time

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

from RatS.base.base_site import Site


class Plex(Site):
def __init__(self, args):
login_form_selector = "//form[@id='user-account-form']"
self.LOGIN_USERNAME_SELECTOR = login_form_selector + "//input[@id='username']"
login_form_selector = "//form"
self.LOGIN_USERNAME_SELECTOR = login_form_selector + "//input[@id='email']"
self.LOGIN_PASSWORD_SELECTOR = login_form_selector + "//input[@id='password']"
self.LOGIN_BUTTON_SELECTOR = login_form_selector + "//button[@type='submit']"
self.LOGIN_METHOD_EMAIL_SELECTOR = "//button[@data-qa-id='signIn--email']"
super(Plex, self).__init__(args)

def _get_login_page_url(self):
self.BASE_URL = self.config[self.site_name]['BASE_URL'] + ":" + self.config[self.site_name]['BASE_PORT']
return "http://{base_url}/web/index.html#!/login".format(base_url=self.BASE_URL)

def _insert_login_credentials(self):
time.sleep(2)
self.browser.find_element_by_xpath(self.LOGIN_METHOD_EMAIL_SELECTOR).click()
time.sleep(0.5)
Site._insert_login_credentials(self)

def _user_is_not_logged_in(self):
return self.USERNAME not in self.browser.page_source

def _parse_configuration(self):
self.MOVIE_SECTION_ID = self._determine_movies_section_id()
self.PLEX_TOKEN = self._determine_plex_token()
self.SERVER_ID = self._determine_server_id()
self.MY_RATINGS_URL = 'http://{base_url}/library/sections/{section_id}/all' \
'?type=1&sort=rating:desc&X-Plex-Container-Start=0&X-Plex-Container-Size=100'.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,
section_id=self.MOVIE_SECTION_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))
wait = ui.WebDriverWait(self.browser, 600)
wait.until(lambda driver: driver.find_element_by_xpath("//button[@data-qa-id='metadataPosterMoreButton']"))

self.browser.get('http://{base_url}/library/sections'.format(base_url=self.BASE_URL))
time.sleep(1)
media_sections = BeautifulSoup(self.browser.page_source, 'html.parser')
time.sleep(1)
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 media_sections.find('directory', attrs={'type': 'movie'})['key']
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 fd1e4ff

Please sign in to comment.