diff --git a/.dockerignore b/.dockerignore index 2eea525..f2ea55a 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1 +1,15 @@ -.env \ No newline at end of file +.dockerignore +.env +.env.sample +.git +.github +.gitignore +.idea +.vscode + +Dockerfile* +README.md + +test + +venv \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c60c3fd..826a647 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -8,7 +8,7 @@ on: paths-ignore: - .gitignore - "*.md" - + jobs: pytest: runs-on: ubuntu-latest @@ -24,25 +24,34 @@ jobs: docker: runs-on: ubuntu-latest needs: pytest + strategy: + matrix: + include: + - dockerfile: Dockerfile.alpine + variant: alpine + - dockerfile: Dockerfile.slim + variant: slim steps: - name: Checkout uses: actions/checkout@v3 - name: Docker meta id: docker_meta - env: - DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }} - if: "${{ env.DOCKER_USERNAME != '' }}" uses: docker/metadata-action@v4 with: - images: ${{ secrets.DOCKER_USERNAME }}/jellyplex-watched # list of Docker images to use as base name for tags + images: | + ${{ secrets.DOCKER_USERNAME }}/jellyplex-watched,enable=${{ secrets.DOCKER_USERNAME != '' }} + # Do not push to ghcr.io on PRs due to permission issues + ghcr.io/${{ github.repository }},enable=${{ github.event_name != 'pull_request' }} tags: | - type=raw,value=latest,enable={{is_default_branch}} - type=ref,event=branch - type=ref,event=pr - type=semver,pattern={{version}} - type=semver,pattern={{major}}.{{minor}} - type=sha + type=raw,value=latest,enable=${{ matrix.variant == 'alpine' && github.ref_name == github.event.repository.default_branch }} + type=raw,value=dev,enable=${{ matrix.variant == 'alpine' && github.ref_name == 'dev' }} + type=raw,value=latest,suffix=-${{ matrix.variant }},enable={{ is_default_branch }} + type=ref,event=branch,suffix=-${{ matrix.variant }} + type=ref,event=pr,suffix=-${{ matrix.variant }} + type=semver,pattern={{ version }},suffix=-${{ matrix.variant }} + type=semver,pattern={{ major }}.{{ minor }},suffix=-${{ matrix.variant }} + type=sha,suffix=-${{ matrix.variant }} - name: Set up QEMU uses: docker/setup-qemu-action@v2 @@ -51,30 +60,40 @@ jobs: uses: docker/setup-buildx-action@v2 - name: Login to DockerHub - if: "${{ steps.docker_meta.outcome == 'success' }}" + env: + DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }} + if: "${{ env.DOCKER_USERNAME != '' }}" uses: docker/login-action@v2 with: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_TOKEN }} + - name: Login to GitHub Container Registry + if: "${{ steps.docker_meta.outcome == 'success' }}" + uses: docker/login-action@v2 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Build id: build - if: "${{ steps.docker_meta.outcome == 'skipped' }}" + if: "${{ steps.docker_meta.outputs.tags == '' }}" uses: docker/build-push-action@v3 with: context: . - file: ./Dockerfile + file: ${{ matrix.dockerfile }} platforms: linux/amd64,linux/arm64 push: false tags: jellyplex-watched:action - + - name: Build Push id: build_push - if: "${{ steps.docker_meta.outcome == 'success' }}" + if: "${{ steps.docker_meta.outputs.tags != '' }}" uses: docker/build-push-action@v3 with: context: . - file: ./Dockerfile + file: ${{ matrix.dockerfile }} platforms: linux/amd64,linux/arm64 push: true tags: ${{ steps.docker_meta.outputs.tags }} diff --git a/Dockerfile.alpine b/Dockerfile.alpine new file mode 100644 index 0000000..5b1a049 --- /dev/null +++ b/Dockerfile.alpine @@ -0,0 +1,41 @@ +FROM python:3-alpine + +ENV DRYRUN 'True' +ENV DEBUG 'True' +ENV DEBUG_LEVEL 'INFO' +ENV SLEEP_DURATION '3600' +ENV LOGFILE 'log.log' + +ENV USER_MAPPING '' +ENV LIBRARY_MAPPING '' + +ENV PLEX_BASEURL '' +ENV PLEX_TOKEN '' +ENV PLEX_USERNAME '' +ENV PLEX_PASSWORD '' +ENV PLEX_SERVERNAME '' + +ENV JELLYFIN_BASEURL '' +ENV JELLYFIN_TOKEN '' + +ENV SYNC_FROM_PLEX_TO_JELLYFIN 'True' +ENV SYNC_FROM_JELLYFIN_TO_PLEX 'True' +ENV SYNC_FROM_PLEX_TO_PLEX 'True' +ENV SYNC_FROM_JELLYFIN_TO_JELLYFIN 'True' + +ENV BLACKLIST_LIBRARY '' +ENV WHITELIST_LIBRARY '' +ENV BLACKLIST_LIBRARY_TYPE '' +ENV WHITELIST_LIBRARY_TYPE '' +ENV BLACKLIST_USERS '' +ENV WHITELIST_USERS '' + +WORKDIR /app + +COPY ./requirements.txt ./ + +RUN pip install --no-cache-dir -r requirements.txt + +COPY . . + +CMD ["python", "-u", "main.py"] diff --git a/Dockerfile b/Dockerfile.slim similarity index 79% rename from Dockerfile rename to Dockerfile.slim index c59692d..f667fe7 100644 --- a/Dockerfile +++ b/Dockerfile.slim @@ -1,46 +1,40 @@ -FROM python:3-slim - -ENV DRYRUN 'True' -ENV DEBUG 'True' -ENV DEBUG_LEVEL 'INFO' -ENV SLEEP_DURATION '3600' -ENV LOGFILE 'log.log' - -ENV USER_MAPPING '' -ENV LIBRARY_MAPPING '' - -ENV PLEX_BASEURL '' -ENV PLEX_TOKEN '' -ENV PLEX_USERNAME '' -ENV PLEX_PASSWORD '' -ENV PLEX_SERVERNAME '' - -ENV JELLYFIN_BASEURL '' -ENV JELLYFIN_TOKEN '' - -ENV SYNC_FROM_PLEX_TO_JELLYFIN 'True' -ENV SYNC_FROM_JELLYFIN_TO_PLEX 'True' -ENV SYNC_FROM_PLEX_TO_PLEX 'True' -ENV SYNC_FROM_JELLYFIN_TO_JELLYFIN 'True' - -ENV BLACKLIST_LIBRARY '' -ENV WHITELIST_LIBRARY '' -ENV BLACKLIST_LIBRARY_TYPE '' -ENV WHITELIST_LIBRARY_TYPE '' -ENV BLACKLIST_USERS '' -ENV WHITELIST_USERS '' - -WORKDIR /app - -RUN apt-get update && \ - apt-get install -y --no-install-recommends \ - build-essential && \ - apt-get clean && \ - rm -rf /var/lib/apt/lists/* - -COPY ./requirements.txt ./ -RUN pip install --no-cache-dir -r requirements.txt - -COPY . . - -CMD ["python", "-u", "main.py"] +FROM python:3-slim + +ENV DRYRUN 'True' +ENV DEBUG 'True' +ENV DEBUG_LEVEL 'INFO' +ENV SLEEP_DURATION '3600' +ENV LOGFILE 'log.log' + +ENV USER_MAPPING '' +ENV LIBRARY_MAPPING '' + +ENV PLEX_BASEURL '' +ENV PLEX_TOKEN '' +ENV PLEX_USERNAME '' +ENV PLEX_PASSWORD '' +ENV PLEX_SERVERNAME '' + +ENV JELLYFIN_BASEURL '' +ENV JELLYFIN_TOKEN '' + +ENV SYNC_FROM_PLEX_TO_JELLYFIN 'True' +ENV SYNC_FROM_JELLYFIN_TO_PLEX 'True' +ENV SYNC_FROM_PLEX_TO_PLEX 'True' +ENV SYNC_FROM_JELLYFIN_TO_JELLYFIN 'True' + +ENV BLACKLIST_LIBRARY '' +ENV WHITELIST_LIBRARY '' +ENV BLACKLIST_LIBRARY_TYPE '' +ENV WHITELIST_LIBRARY_TYPE '' +ENV BLACKLIST_USERS '' +ENV WHITELIST_USERS '' + +WORKDIR /app + +COPY ./requirements.txt ./ +RUN pip install --no-cache-dir -r requirements.txt + +COPY . . + +CMD ["python", "-u", "main.py"] diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..e3783f1 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,31 @@ +version: '3' + +services: + jellyplex-watched: + image: luigi311/jellyplex-watched:latest + container_name: jellyplex-watched + restart: always + environment: + - DRYRUN=True + - DEBUG=True + - DEBUG_LEVEL=info + - RUN_ONLY_ONCE=False + - SLEEP_DURATION=3600 + - LOGFILE=/tmp/log.log + - USER_MAPPING= + - LIBRARY_MAPPING={"TV Shows":"Shows"} + - BLACKLIST_LIBRARY= + - WHITELIST_LIBRARY= + - BLACKLIST_LIBRARY_TYPE= + - WHITELIST_LIBRARY_TYPE= + - BLACKLIST_USERS= + - WHITELIST_USERS= + - PLEX_BASEURL= + - PLEX_TOKEN= + - JELLYFIN_BASEURL= + - JELLYFIN_TOKEN= + - SSL_BYPASS=True + - SYNC_FROM_PLEX_TO_JELLYFIN=True + - SYNC_FROM_JELLYFIN_TO_PLEX=True + - SYNC_FROM_PLEX_TO_PLEX=True + - SYNC_FROM_JELLYFIN_TO_JELLYFIN=True diff --git a/requirements.txt b/requirements.txt index b5d8e22..de75d94 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -plexapi -requests -python-dotenv -aiohttp +PlexAPI==4.13.4 +requests==2.28.2 +python-dotenv==1.0.0 +aiohttp==3.8.4 diff --git a/src/black_white.py b/src/black_white.py index 6b142c0..e32ee08 100644 --- a/src/black_white.py +++ b/src/black_white.py @@ -11,18 +11,20 @@ def setup_black_white_lists( library_mapping=None, user_mapping=None, ): - blacklist_library, blacklist_library_type, blacklist_users = setup_black_lists( + blacklist_library, blacklist_library_type, blacklist_users = setup_x_lists( blacklist_library, blacklist_library_type, blacklist_users, + "White", library_mapping, user_mapping, ) - whitelist_library, whitelist_library_type, whitelist_users = setup_white_lists( + whitelist_library, whitelist_library_type, whitelist_users = setup_x_lists( whitelist_library, whitelist_library_type, whitelist_users, + "Black", library_mapping, user_mapping, ) @@ -36,104 +38,93 @@ def setup_black_white_lists( whitelist_users, ) - -def setup_black_lists( - blacklist_library, - blacklist_library_type, - blacklist_users, +def setup_x_lists( + xlist_library, + xlist_library_type, + xlist_users, + xlist_type, library_mapping=None, user_mapping=None, ): - if blacklist_library: - if len(blacklist_library) > 0: - blacklist_library = blacklist_library.split(",") - blacklist_library = [x.strip() for x in blacklist_library] + if xlist_library: + if len(xlist_library) > 0: + xlist_library = xlist_library.split(",") + xlist_library = [x.strip() for x in xlist_library] if library_mapping: temp_library = [] - for library in blacklist_library: + for library in xlist_library: library_other = search_mapping(library_mapping, library) if library_other: temp_library.append(library_other) - blacklist_library = blacklist_library + temp_library + xlist_library = xlist_library + temp_library else: - blacklist_library = [] - logger(f"Blacklist Library: {blacklist_library}", 1) + xlist_library = [] + logger(f"{xlist_type}list Library: {xlist_library}", 1) - if blacklist_library_type: - if len(blacklist_library_type) > 0: - blacklist_library_type = blacklist_library_type.split(",") - blacklist_library_type = [x.lower().strip() for x in blacklist_library_type] + if xlist_library_type: + if len(xlist_library_type) > 0: + xlist_library_type = xlist_library_type.split(",") + xlist_library_type = [x.lower().strip() for x in xlist_library_type] else: - blacklist_library_type = [] - logger(f"Blacklist Library Type: {blacklist_library_type}", 1) + xlist_library_type = [] + logger(f"{xlist_type}list Library Type: {xlist_library_type}", 1) - if blacklist_users: - if len(blacklist_users) > 0: - blacklist_users = blacklist_users.split(",") - blacklist_users = [x.lower().strip() for x in blacklist_users] + if xlist_users: + if len(xlist_users) > 0: + xlist_users = xlist_users.split(",") + xlist_users = [x.lower().strip() for x in xlist_users] if user_mapping: temp_users = [] - for user in blacklist_users: + for user in xlist_users: user_other = search_mapping(user_mapping, user) if user_other: temp_users.append(user_other) - blacklist_users = blacklist_users + temp_users - else: - blacklist_users = [] - logger(f"Blacklist Users: {blacklist_users}", 1) - - return blacklist_library, blacklist_library_type, blacklist_users - - -def setup_white_lists( - whitelist_library, - whitelist_library_type, - whitelist_users, - library_mapping=None, - user_mapping=None, -): - if whitelist_library: - if len(whitelist_library) > 0: - whitelist_library = whitelist_library.split(",") - whitelist_library = [x.strip() for x in whitelist_library] - if library_mapping: - temp_library = [] - for library in whitelist_library: - library_other = search_mapping(library_mapping, library) - if library_other: - temp_library.append(library_other) - - whitelist_library = whitelist_library + temp_library - else: - whitelist_library = [] - logger(f"Whitelist Library: {whitelist_library}", 1) - - if whitelist_library_type: - if len(whitelist_library_type) > 0: - whitelist_library_type = whitelist_library_type.split(",") - whitelist_library_type = [x.lower().strip() for x in whitelist_library_type] - else: - whitelist_library_type = [] - logger(f"Whitelist Library Type: {whitelist_library_type}", 1) - - if whitelist_users: - if len(whitelist_users) > 0: - whitelist_users = whitelist_users.split(",") - whitelist_users = [x.lower().strip() for x in whitelist_users] - if user_mapping: - temp_users = [] - for user in whitelist_users: - user_other = search_mapping(user_mapping, user) - if user_other: - temp_users.append(user_other) - - whitelist_users = whitelist_users + temp_users + xlist_users = xlist_users + temp_users else: - whitelist_users = [] + xlist_users = [] else: - whitelist_users = [] - logger(f"Whitelist Users: {whitelist_users}", 1) + xlist_users = [] + logger(f"{xlist_type}list Users: {xlist_users}", 1) + + return xlist_library, xlist_library_type, xlist_users + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - return whitelist_library, whitelist_library_type, whitelist_users diff --git a/src/main.py b/src/main.py index f308310..715e5d8 100644 --- a/src/main.py +++ b/src/main.py @@ -40,15 +40,23 @@ def setup_users( # Check if users is none or empty if output_server_1_users is None or len(output_server_1_users) == 0: - raise Exception( - f"No users found for server 1 {server_1[0]}, users found {users}, filtered users {users_filtered}, server 1 users {server_1[1].users}" + logger( + f"No users found for server 1 {server_1[0]}, users: {server_1_users}, overlapping users {users}, filtered users {users_filtered}, server 1 users {server_1[1].users}" ) if output_server_2_users is None or len(output_server_2_users) == 0: - raise Exception( - f"No users found for server 2 {server_2[0]}, users found {users} filtered users {users_filtered}, server 2 users {server_2[1].users}" + logger( + f"No users found for server 2 {server_2[0]}, users: {server_2_users}, overlapping users {users} filtered users {users_filtered}, server 2 users {server_2[1].users}" ) + if ( + output_server_1_users is None + or len(output_server_1_users) == 0 + or output_server_2_users is None + or len(output_server_2_users) == 0 + ): + raise Exception("No users found for one or both servers") + logger(f"Server 1 users: {output_server_1_users}", 1) logger(f"Server 2 users: {output_server_2_users}", 1) diff --git a/src/plex.py b/src/plex.py index 889078d..0bcdf7b 100644 --- a/src/plex.py +++ b/src/plex.py @@ -126,7 +126,7 @@ def get_user_library_watched_show(show): def get_user_library_watched(user, user_plex, library): try: - user_name = user.title.lower() + user_name = user.username.lower() if user.username else user.title.lower() user_watched = {} user_watched[user_name] = {} @@ -509,10 +509,16 @@ class Plex: user_other = search_mapping(user_mapping, user) for index, value in enumerate(self.users): - if user.lower() == value.title.lower(): + username_title = ( + value.username.lower() + if value.username + else value.title.lower() + ) + + if user.lower() == username_title: user = self.users[index] break - elif user_other and user_other.lower() == value.title.lower(): + elif user_other and user_other.lower() == username_title: user = self.users[index] break diff --git a/src/users.py b/src/users.py index 6075be8..4f1e280 100644 --- a/src/users.py +++ b/src/users.py @@ -11,7 +11,11 @@ def generate_user_list(server): server_users = [] if server_type == "plex": - server_users = [x.title.lower() for x in server_connection.users] + for user in server_connection.users: + server_users.append( + user.username.lower() if user.username else user.title.lower() + ) + elif server_type == "jellyfin": server_users = [key.lower() for key in server_connection.users.keys()] @@ -66,9 +70,13 @@ def generate_server_users(server, users): if server[0] == "plex": server_users = [] for plex_user in server[1].users: + username_title = ( + plex_user.username if plex_user.username else plex_user.title + ) + if ( - plex_user.title.lower() in users.keys() - or plex_user.title.lower() in users.values() + username_title.lower() in users.keys() + or username_title.lower() in users.values() ): server_users.append(plex_user) elif server[0] == "jellyfin": diff --git a/test/requirements.txt b/test/requirements.txt index 4f76727..7556140 100644 --- a/test/requirements.txt +++ b/test/requirements.txt @@ -1 +1 @@ -pytest +pytest==7.3.0