diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml new file mode 100644 index 00000000..9e84bdd8 --- /dev/null +++ b/.github/workflows/nightly.yml @@ -0,0 +1,44 @@ +name: Docker Nightly Release + +on: + push: + branches: [ nightly ] + pull_request: + branches: [ nightly ] + +jobs: + + docker-develop: + runs-on: ubuntu-latest + + steps: + + - name: Check Out Repo + uses: actions/checkout@v2 + with: + ref: nightly + + - name: Login to Docker Hub + uses: docker/login-action@v1 + with: + username: ${{ secrets.DOCKER_HUB_USERNAME }} + password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }} + + - name: Set up QEMU + uses: docker/setup-qemu-action@master + with: + platforms: all + + - name: Set up Docker Buildx + id: buildx + uses: docker/setup-buildx-action@v1 + + - name: Build and push + id: docker_build + uses: docker/build-push-action@v2 + with: + context: ./ + file: ./Dockerfile + platforms: linux/amd64,linux/arm64 + push: true + tags: ${{ secrets.DOCKER_HUB_USERNAME }}/plex-meta-manager:nightly diff --git a/VERSION b/VERSION index b0fc2f90..1192d1e0 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.15.1-develop91 +1.15.1-develop94 diff --git a/config/config.yml.template b/config/config.yml.template index cdcb5dd7..6f961daf 100644 --- a/config/config.yml.template +++ b/config/config.yml.template @@ -73,6 +73,10 @@ tautulli: # Can be individually specified apikey: ################################ omdb: apikey: ######## + cache_expiration: 60 +mdblist: + apikey: ######################### + cache_expiration: 60 notifiarr: apikey: #################################### anidb: # Not required for AniDB builders unless you want mature content diff --git a/modules/builder.py b/modules/builder.py index dba51b4c..de90178a 100644 --- a/modules/builder.py +++ b/modules/builder.py @@ -97,7 +97,7 @@ ignored_details = [ "delete_not_scheduled", "tmdb_person", "build_collection", "collection_order", "collection_level", "validate_builders", "libraries", "sync_to_users", "collection_name", "playlist_name", "name", "blank_collection" ] -details = ["ignore_ids", "ignore_imdb_ids", "server_preroll", "changes_webhooks", "collection_mode", "limit", +details = ["ignore_ids", "ignore_imdb_ids", "server_preroll", "changes_webhooks", "collection_mode", "limit", "url_theme", "file_theme" "minimum_items", "label", "album_sorting", "cache_builders"] + boolean_details + scheduled_boolean + string_details collectionless_details = ["collection_order", "plex_collectionless", "label", "label_sync_mode", "test"] + \ poster_details + background_details + summary_details + string_details @@ -183,7 +183,8 @@ episode_parts_only = ["plex_pilots"] parts_collection_valid = [ "filters", "plex_all", "plex_search", "trakt_list", "trakt_list_details", "collection_mode", "label", "visible_library", "limit", "visible_home", "visible_shared", "show_missing", "save_missing", "missing_only_released", "server_preroll", "changes_webhooks", - "item_lock_background", "item_lock_poster", "item_lock_title", "item_refresh", "item_refresh_delay", "imdb_list", "cache_builders" + "item_lock_background", "item_lock_poster", "item_lock_title", "item_refresh", "item_refresh_delay", "imdb_list", "cache_builders", + "url_theme", "file_theme" ] + episode_parts_only + summary_details + poster_details + background_details + string_details playlist_attributes = [ "filters", "name_mapping", "show_filtered", "show_missing", "save_missing", @@ -766,6 +767,13 @@ class CollectionBuilder: logger.error(f"{self.Type} Error: Background Path Does Not Exist: {os.path.abspath(method_data)}") def _details(self, method_name, method_data, method_final, methods): + if method_name == "url_theme": + self.url_theme = method_data + elif method_name == "file_theme": + if os.path.exists(os.path.abspath(method_data)): + self.file_theme = os.path.abspath(method_data) + else: + logger.error(f"{self.Type} Error: Theme Path Does Not Exist: {os.path.abspath(method_data)}") if method_name == "collection_mode": self.details[method_name] = util.check_collection_mode(method_data) elif method_name == "minimum_items": @@ -2393,7 +2401,6 @@ class CollectionBuilder: sync_tags = self.details["label.sync"] if "label.sync" in self.details else None self.library.edit_tags("label", self.obj, add_tags=add_tags, remove_tags=remove_tags, sync_tags=sync_tags) - poster_image = None background_image = None asset_location = None @@ -2487,6 +2494,11 @@ class CollectionBuilder: if self.collection_poster or self.collection_background: self.library.upload_images(self.obj, poster=self.collection_poster, background=self.collection_background) + if self.url_theme: + self.library.upload_theme(url=self.url_theme) + elif self.file_theme: + self.library.upload_theme(filepath=self.file_theme) + def sort_collection(self): logger.info("") logger.separator(f"Sorting {self.name} {self.Type}", space=False, border=False) diff --git a/modules/plex.py b/modules/plex.py index be8abd0c..266287c6 100644 --- a/modules/plex.py +++ b/modules/plex.py @@ -476,6 +476,13 @@ class Plex(Library): self._all_items = results return results + def upload_theme(self, collection, url=None, filepath=None): + key = f"/library/metadata/{collection.ratingKey}/themes" + if url: + self.PlexServer.query(f"{key}?url={parse.quote_plus(url)}", method=self.PlexServer._session.post) + elif filepath: + self.PlexServer.query(key, method=self.PlexServer._session.post, data=open(filepath, 'rb').read()) + @retry(stop_max_attempt_number=6, wait_fixed=10000, retry_on_exception=util.retry_if_not_plex) def create_playlist(self, name, items): return self.PlexServer.createPlaylist(name, items=items) diff --git a/modules/trakt.py b/modules/trakt.py index 9710f8ae..d290013f 100644 --- a/modules/trakt.py +++ b/modules/trakt.py @@ -234,7 +234,7 @@ class Trakt: raise Failed(f"Trakt Error: failed to fetch {media_type} Recommendations") if len(items) == 0: raise Failed(f"Trakt Error: no {media_type} Recommendations were found") - return self._parse(items, item_type="movie" if is_movie else "show") + return self._parse(items, typeless=True, item_type="movie" if is_movie else "show") def _pagenation(self, pagenation, amount, is_movie): items = self._request(f"/{'movies' if is_movie else 'shows'}/{pagenation}?limit={amount}") diff --git a/plex_meta_manager.py b/plex_meta_manager.py index 256e1e3a..1f4459a7 100644 --- a/plex_meta_manager.py +++ b/plex_meta_manager.py @@ -433,13 +433,10 @@ def library_operations(config, library): num_edited = 0 for i, item in enumerate(tracks, 1): logger.ghost(f"Processing Track: {i}/{len(tracks)} {item.title}") - try: - if not item.title and item.titleSort: - library.edit_query(item, {"title.locked": 1, "title.value": item.titleSort}) - num_edited += 1 - logger.info(f"Track: {item.titleSort} was updated with sort title") - except Failed as e: - logger.error(e) + if not item.title and item.titleSort: + library.edit_query(item, {"title.locked": 1, "title.value": item.titleSort}) + num_edited += 1 + logger.info(f"Track: {item.titleSort} was updated with sort title") logger.info(f"{len(tracks)} Tracks Processed; {num_edited} Blank Track Titles Updated") tmdb_collections = {} @@ -701,9 +698,8 @@ def library_operations(config, library): logger.info("") unmanaged_collections = [] for col in library.get_all_collections(): - if (library.delete_collections_with_less is not None - and (library.delete_collections_with_less == 0 or col.childCount < library.delete_collections_with_less)) \ - or (col.title not in library.collections and library.delete_unmanaged_collections): + if (library.delete_collections_with_less and col.childCount < library.delete_collections_with_less) \ + or (library.delete_unmanaged_collections and col.title not in library.collections): library.query(col.delete) logger.info(f"{col.title} Deleted") elif col.title not in library.collections: