From 994d529f5919518834ce2c651df913573e37369b Mon Sep 17 00:00:00 2001 From: Luigi311 Date: Wed, 6 Dec 2023 12:54:33 -0700 Subject: [PATCH 1/4] Action: Add test Spins up jellyfin and plex containers to test against --- .github/workflows/ci.yml | 43 +++++++++++++++++++- src/jellyfin.py | 10 +++-- src/main.py | 45 +++++++++++++-------- test/ci.env | 86 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 163 insertions(+), 21 deletions(-) create mode 100644 test/ci.env diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5b9be46..dad43c3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,9 +21,50 @@ jobs: - name: "Run tests" run: pytest -vvv + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - name: "Install dependencies" + run: | + pip install -r requirements.txt + sudo apt update && sudo apt install -y docker-compose + + - name: "Checkout JellyPlex-Watched-CI" + uses: actions/checkout@v2 + with: + repository: luigi311/JellyPlex-Watched-CI + path: JellyPlex-Watched-CI + + - name: "Start containers" + run: | + export PGID=$(id -g) + export PUID=$(id -u) + + sudo chown -R $PUID:$PGID JellyPlex-Watched-CI + + docker-compose -f JellyPlex-Watched-CI/plex/docker-compose.yml up -d + docker-compose -f JellyPlex-Watched-CI/jellyfin/docker-compose.yml up -d + # Wait for containers to start + sleep 15 + docker-compose -f JellyPlex-Watched-CI/plex/docker-compose.yml logs + docker-compose -f JellyPlex-Watched-CI/jellyfin/docker-compose.yml logs + + - name: "Run tests" + run: | + # Move test/.env to root + mv test/ci.env .env + # Run script + python main.py + + cat mark.log + docker: runs-on: ubuntu-latest - needs: pytest + needs: + - pytest + - test env: DEFAULT_VARIANT: alpine strategy: diff --git a/src/jellyfin.py b/src/jellyfin.py index faaa144..41ddbc9 100644 --- a/src/jellyfin.py +++ b/src/jellyfin.py @@ -100,7 +100,7 @@ class Jellyfin: return await self.query(query, query_type, session, identifiers) results = None - + authorization = ( "MediaBrowser , " 'Client="other", ' @@ -108,8 +108,12 @@ class Jellyfin: 'DeviceId="script", ' 'Version="0.0.0"' ) - headers = {"Accept": "application/json", "X-Emby-Token": self.token, "X-Emby-Authorization": authorization} - + headers = { + "Accept": "application/json", + "X-Emby-Token": self.token, + "X-Emby-Authorization": authorization, + } + if query_type == "get": async with session.get( self.baseurl + query, headers=headers diff --git a/src/main.py b/src/main.py index 562dc46..05b5c96 100644 --- a/src/main.py +++ b/src/main.py @@ -83,17 +83,21 @@ def generate_server_connections(): ) for i, url in enumerate(plex_baseurl): + server = Plex( + baseurl=url.strip(), + token=plex_token[i].strip(), + username=None, + password=None, + servername=None, + ssl_bypass=ssl_bypass, + ) + + logger(f"Plex Server {i} info: {server.info()}", 3) + servers.append( ( "plex", - Plex( - baseurl=url.strip(), - token=plex_token[i].strip(), - username=None, - password=None, - servername=None, - ssl_bypass=ssl_bypass, - ), + server, ) ) @@ -110,17 +114,20 @@ def generate_server_connections(): ) for i, username in enumerate(plex_username): + server = Plex( + baseurl=None, + token=None, + username=username.strip(), + password=plex_password[i].strip(), + servername=plex_servername[i].strip(), + ssl_bypass=ssl_bypass, + ) + + logger(f"Plex Server {i} info: {server.info()}", 3) servers.append( ( "plex", - Plex( - baseurl=None, - token=None, - username=username.strip(), - password=plex_password[i].strip(), - servername=plex_servername[i].strip(), - ssl_bypass=ssl_bypass, - ), + server, ) ) @@ -140,10 +147,14 @@ def generate_server_connections(): baseurl = baseurl.strip() if baseurl[-1] == "/": baseurl = baseurl[:-1] + + server = Jellyfin(baseurl=baseurl, token=jellyfin_token[i].strip()) + + logger(f"Jellyfin Server {i} info: {server.info()}", 3) servers.append( ( "jellyfin", - Jellyfin(baseurl=baseurl, token=jellyfin_token[i].strip()), + server, ) ) diff --git a/test/ci.env b/test/ci.env new file mode 100644 index 0000000..ba9bd28 --- /dev/null +++ b/test/ci.env @@ -0,0 +1,86 @@ +# Global Settings + +## Do not mark any shows/movies as played and instead just output to log if they would of been marked. +DRYRUN = "False" + +## Additional logging information +DEBUG = "True" + +## Debugging level, "info" is default, "debug" is more verbose +DEBUG_LEVEL = "debug" + +## If set to true then the script will only run once and then exit +RUN_ONLY_ONCE = "True" + +## How often to run the script in seconds +SLEEP_DURATION = 10 + +## Log file where all output will be written to +LOG_FILE = "log.log" + +## Mark file where all shows/movies that have been marked as played will be written to +MARK_FILE = "mark.log" + +## Timeout for requests for jellyfin +REQUEST_TIMEOUT = 300 + +## Max threads for processing +MAX_THREADS = 2 + +## Map usernames between servers in the event that they are different, order does not matter +## Comma seperated for multiple options +USER_MAPPING = {"JellyUser":"jellyplex_watched"} + +## Map libraries between servers in the even that they are different, order does not matter +## Comma seperated for multiple options +LIBRARY_MAPPING = { "Shows": "TV Shows" } + + +## Blacklisting/Whitelisting libraries, library types such as Movies/TV Shows, and users. Mappings apply so if the mapping for the user or library exist then both will be excluded. +## Comma seperated for multiple options +#BLACKLIST_LIBRARY = "" +#WHITELIST_LIBRARY = "Movies" +#BLACKLIST_LIBRARY_TYPE = "Series" +#WHITELIST_LIBRARY_TYPE = "Movies, movie" +#BLACKLIST_USERS = "" +WHITELIST_USERS = "jellyplex_watched" + + + +# Plex + +## Recommended to use token as it is faster to connect as it is direct to the server instead of going through the plex servers +## URL of the plex server, use hostname or IP address if the hostname is not resolving correctly +## Comma seperated list for multiple servers +PLEX_BASEURL = "https://localhost:32400" + +## Plex token https://support.plex.tv/articles/204059436-finding-an-authentication-token-x-plex-token/ +## Comma seperated list for multiple servers +PLEX_TOKEN = "mVaCzSyd78uoWkCBzZ_Y" + +## If not using plex token then use username and password of the server admin along with the servername +## Comma seperated for multiple options +#PLEX_USERNAME = "PlexUser, PlexUser2" +#PLEX_PASSWORD = "SuperSecret, SuperSecret2" +#PLEX_SERVERNAME = "Plex Server1, Plex Server2" + +## Skip hostname validation for ssl certificates. +## Set to True if running into ssl certificate errors +SSL_BYPASS = "True" + +## control the direction of syncing. e.g. SYNC_FROM_PLEX_TO_JELLYFIN set to true will cause the updates from plex +## to be updated in jellyfin. SYNC_FROM_PLEX_TO_PLEX set to true will sync updates between multiple plex servers +SYNC_FROM_PLEX_TO_JELLYFIN = "True" +SYNC_FROM_JELLYFIN_TO_PLEX = "True" +SYNC_FROM_PLEX_TO_PLEX = "True" +SYNC_FROM_JELLYFIN_TO_JELLYFIN = "True" + +# Jellyfin + +## Jellyfin server URL, use hostname or IP address if the hostname is not resolving correctly +## Comma seperated list for multiple servers +JELLYFIN_BASEURL = "http://localhost:8096" + +## Jellyfin api token, created manually by logging in to the jellyfin server admin dashboard and creating an api key +## Comma seperated list for multiple servers +JELLYFIN_TOKEN = "d773c4db3ecc4b028fc0904d9694804c" From b46d4a71661123a3e7d52de04171ad1b76155bd6 Mon Sep 17 00:00:00 2001 From: Luigi311 Date: Fri, 8 Dec 2023 11:57:30 -0700 Subject: [PATCH 2/4] Show average time on exit Signed-off-by: Luigi311 --- src/main.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main.py b/src/main.py index 05b5c96..fa99a63 100644 --- a/src/main.py +++ b/src/main.py @@ -422,5 +422,7 @@ def main(): sleep(sleep_duration) except KeyboardInterrupt: + if len(times) > 0: + logger(f"Average time: {sum(times) / len(times)}", 0) logger("Exiting", log_type=0) os._exit(0) From 0190788658723bd09cecf0747fe5e157cc2fbf07 Mon Sep 17 00:00:00 2001 From: Luigi311 Date: Fri, 8 Dec 2023 14:16:43 -0700 Subject: [PATCH 3/4] Plex: Use updateTimeline instead of updateProgress Not all accounts have access to updateProgress, so use updateTimeline instead Signed-off-by: Luigi311 --- src/plex.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plex.py b/src/plex.py index 87b2b3a..01049c2 100644 --- a/src/plex.py +++ b/src/plex.py @@ -308,7 +308,7 @@ def update_user_watched(user, user_plex, library, videos, dryrun): msg = f"Plex: {movies_search.title} as partially watched for {floor(video_status['time'] / 60_000)} minutes for {user.title} in {library}" if not dryrun: logger(msg, 5) - movies_search.updateProgress(video_status["time"]) + movies_search.updateTimeline(video_status["time"]) else: logger(msg, 6) @@ -353,7 +353,7 @@ def update_user_watched(user, user_plex, library, videos, dryrun): msg = f"Plex: {show_search.title} {episode_search.title} as partially watched for {floor(video_status['time'] / 60_000)} minutes for {user.title} in {library}" if not dryrun: logger(msg, 5) - episode_search.updateProgress(video_status["time"]) + episode_search.updateTimeline(video_status["time"]) else: logger(msg, 6) From 2d00d8cb3ed9769a9d6a8c14d5e1c35fe3040b09 Mon Sep 17 00:00:00 2001 From: Luigi311 Date: Sat, 9 Dec 2023 18:37:23 -0700 Subject: [PATCH 4/4] README: Add troubleshooting/Issues Signed-off-by: Luigi311 --- README.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/README.md b/README.md index 8b6cfdd..476f933 100644 --- a/README.md +++ b/README.md @@ -96,6 +96,15 @@ Full list of configuration options can be found in the [.env.sample](.env.sample docker run --rm -it -v "$(pwd)/.env:/app/.env" luigi311/jellyplex-watched:latest ``` +## Troubleshooting/Issues + +* Jellyfin + * Attempt to decode JSON with unexpected mimetype, make sure you enable remote access or add your docker subnet to lan networks in jellyfin settings + +* Configuration + * Do not use quotes around variables in docker compose + + ## Contributing I am open to receiving pull requests. If you are submitting a pull request, please make sure run it locally for a day or two to make sure it is working as expected and stable. Make all pull requests against the dev branch and nothing will be merged into the main without going through the lower branches.