From 583239deb2f35be60497fb1c7ac2f547c891f8f3 Mon Sep 17 00:00:00 2001 From: Romain Lecat Date: Mon, 16 Feb 2026 11:07:30 +0100 Subject: [PATCH] Custom build with patches --- .gitea/workflows/build.yml | 67 ++ Dockerfile | 78 ++- ...k-for-AvistaZ-family-language-filter.patch | 663 ++++++++++++++++++ 3 files changed, 787 insertions(+), 21 deletions(-) create mode 100644 .gitea/workflows/build.yml create mode 100644 patches/0001-Fork-for-AvistaZ-family-language-filter.patch diff --git a/.gitea/workflows/build.yml b/.gitea/workflows/build.yml new file mode 100644 index 0000000..22af7a5 --- /dev/null +++ b/.gitea/workflows/build.yml @@ -0,0 +1,67 @@ +name: Build Prowlarr Container + +on: + push: + branches: + - main + - develop + workflow_dispatch: + +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Set build variables + id: vars + run: | + # Date ISO 8601 + echo "build_date=$(date -u +%Y-%m-%dT%H:%M:%S%z)" >> "$GITHUB_OUTPUT" + + # Short SHA + echo "sha_short=$(echo ${GITHUB_SHA} | cut -c1-7)" >> "$GITHUB_OUTPUT" + + # Prowlarr version from upstream API + PROWLARR_BRANCH="develop" + if [ "${GITHUB_REF_NAME}" = "main" ]; then + PROWLARR_BRANCH="master" + fi + echo "prowlarr_branch=${PROWLARR_BRANCH}" >> "$GITHUB_OUTPUT" + + PROWLARR_VERSION=$(curl -sL "https://prowlarr.servarr.com/v1/update/${PROWLARR_BRANCH}/changes?runtime=netcore&os=linuxmusl" | jq -r '.[0].version') + echo "prowlarr_version=${PROWLARR_VERSION}" >> "$GITHUB_OUTPUT" + + # Image name + REGISTRY="${{ vars.OCI_REGISTRY }}" + # Remove trailing slash if present + REGISTRY="${REGISTRY%/}" + echo "image_name=${REGISTRY}/prowlarr" >> "$GITHUB_OUTPUT" + + - name: Login to OCI Registry + uses: docker/login-action@v3 + with: + registry: ${{ vars.OCI_REGISTRY }} + username: ${{ vars.OCI_USERNAME }} + password: ${{ vars.OCI_PASSWORD }} + + - name: Build and push + uses: docker/build-push-action@v6 + with: + context: . + file: ./Dockerfile + push: true + build-args: | + BUILD_DATE=${{ steps.vars.outputs.build_date }} + VERSION=${{ steps.vars.outputs.prowlarr_version }}-custom + PROWLARR_BRANCH=${{ steps.vars.outputs.prowlarr_branch }} + tags: | + ${{ steps.vars.outputs.image_name }}:latest + ${{ steps.vars.outputs.image_name }}:${{ steps.vars.outputs.sha_short }} + ${{ steps.vars.outputs.image_name }}:${{ steps.vars.outputs.prowlarr_version }}-custom + cache-from: type=gha + cache-to: type=gha,mode=max diff --git a/Dockerfile b/Dockerfile index 5a230ac..b9da870 100755 --- a/Dockerfile +++ b/Dockerfile @@ -1,47 +1,83 @@ # syntax=docker/dockerfile:1 +#=========================== +# Stage 1: Build Prowlarr from source +#=========================== +FROM mcr.microsoft.com/dotnet/sdk:8.0-alpine AS build + +# Install build dependencies +RUN apk add --no-cache \ + bash \ + git \ + curl \ + icu-libs \ + nodejs \ + npm \ + yarn + +# Arguments for upstream source +ARG PROWLARR_BRANCH="develop" +ARG PROWLARR_REPO="https://github.com/Prowlarr/Prowlarr.git" + +WORKDIR /src + +# Clone upstream Prowlarr +RUN git clone --depth 1 --branch "${PROWLARR_BRANCH}" "${PROWLARR_REPO}" . + +# Copy and apply patches +COPY patches/ /tmp/patches/ +RUN git config user.email "build@local" && \ + git config user.name "Build" && \ + git am /tmp/patches/*.patch + +# Install frontend dependencies and build +RUN yarn install --frozen-lockfile --network-timeout 120000 +RUN yarn run build --env production + +# Build backend for linux-musl-x64 (Alpine) +RUN bash build.sh --backend -r linux-musl-x64 -f net8.0 + +# Package +RUN bash build.sh --packages -r linux-musl-x64 -f net8.0 + +#=========================== +# Stage 2: Runtime +#=========================== FROM ghcr.io/linuxserver/baseimage-alpine:3.23 # set version label ARG BUILD_DATE ARG VERSION -ARG PROWLARR_RELEASE -LABEL build_version="Linuxserver.io version:- ${VERSION} Build-date:- ${BUILD_DATE}" -LABEL maintainer="Roxedus,thespad" +ARG PROWLARR_BRANCH="develop" +LABEL build_version="Custom build version:- ${VERSION} Build-date:- ${BUILD_DATE}" +LABEL maintainer="romain" # environment settings -ARG PROWLARR_BRANCH="master" ENV XDG_CONFIG_HOME="/config/xdg" \ COMPlus_EnableDiagnostics=0 \ TMPDIR=/run/prowlarr-temp RUN \ - echo "**** install packages ****" && \ + echo "**** install runtime packages ****" && \ apk add -U --upgrade --no-cache \ icu-libs \ sqlite-libs \ - xmlstarlet && \ - echo "**** install prowlarr ****" && \ - mkdir -p /app/prowlarr/bin && \ - if [ -z ${PROWLARR_RELEASE+x} ]; then \ - PROWLARR_RELEASE=$(curl -sL "https://prowlarr.servarr.com/v1/update/${PROWLARR_BRANCH}/changes?runtime=netcore&os=linuxmusl" \ - | jq -r '.[0].version'); \ - fi && \ - curl -o \ - /tmp/prowlarr.tar.gz -L \ - "https://prowlarr.servarr.com/v1/update/${PROWLARR_BRANCH}/updatefile?version=${PROWLARR_RELEASE}&os=linuxmusl&runtime=netcore&arch=x64" && \ - tar xzf \ - /tmp/prowlarr.tar.gz -C \ - /app/prowlarr/bin --strip-components=1 && \ - echo -e "UpdateMethod=docker\nBranch=${PROWLARR_BRANCH}\nPackageVersion=${VERSION}\nPackageAuthor=[linuxserver.io](https://www.linuxserver.io/)" > /app/prowlarr/package_info && \ - printf "Linuxserver.io version: ${VERSION}\nBuild-date: ${BUILD_DATE}" > /build_version && \ + xmlstarlet + +# Copy compiled Prowlarr from build stage +RUN mkdir -p /app/prowlarr/bin +COPY --from=build /src/_artifacts/linux-musl-x64/net8.0/Prowlarr/ /app/prowlarr/bin/ + +# Write package info +RUN echo -e "UpdateMethod=docker\nBranch=${PROWLARR_BRANCH}\nPackageVersion=${VERSION}\nPackageAuthor=custom-build" > /app/prowlarr/package_info && \ + printf "Custom build version: ${VERSION}\nBuild-date: ${BUILD_DATE}" > /build_version && \ echo "**** cleanup ****" && \ rm -rf \ /app/prowlarr/bin/Prowlarr.Update \ /tmp/* \ /var/tmp/* -# copy local files +# copy local files (s6 services) COPY root/ / # ports and volumes diff --git a/patches/0001-Fork-for-AvistaZ-family-language-filter.patch b/patches/0001-Fork-for-AvistaZ-family-language-filter.patch new file mode 100644 index 0000000..f33b4a1 --- /dev/null +++ b/patches/0001-Fork-for-AvistaZ-family-language-filter.patch @@ -0,0 +1,663 @@ +From f7ad90f98669386f4abfde0345a43e4ce13b1dfa Mon Sep 17 00:00:00 2001 +From: Romain Lecat +Date: Mon, 16 Feb 2026 10:49:13 +0100 +Subject: [PATCH] Fork for AvistaZ family language filter + +--- + .../Indexers/Definitions/AvistaZ.cs | 26 +- + .../Definitions/Avistaz/AvistazBase.cs | 3 +- + .../Definitions/Avistaz/AvistazSettings.cs | 359 ++++++++++++++++++ + .../Definitions/Avistaz/CinemaZSettings.cs | 21 + + .../Definitions/Avistaz/PrivateHDSettings.cs | 21 + + .../Indexers/Definitions/CinemaZ.cs | 42 +- + .../Indexers/Definitions/ExoticaZ.cs | 2 +- + .../Indexers/Definitions/PrivateHD.cs | 42 +- + 8 files changed, 511 insertions(+), 5 deletions(-) + create mode 100644 src/NzbDrone.Core/Indexers/Definitions/Avistaz/CinemaZSettings.cs + create mode 100644 src/NzbDrone.Core/Indexers/Definitions/Avistaz/PrivateHDSettings.cs + +diff --git a/src/NzbDrone.Core/Indexers/Definitions/AvistaZ.cs b/src/NzbDrone.Core/Indexers/Definitions/AvistaZ.cs +index cc1be98b5..edb0ab464 100644 +--- a/src/NzbDrone.Core/Indexers/Definitions/AvistaZ.cs ++++ b/src/NzbDrone.Core/Indexers/Definitions/AvistaZ.cs +@@ -1,4 +1,5 @@ + using System.Collections.Generic; ++using System.Linq; + using NLog; + using NzbDrone.Common.Extensions; + using NzbDrone.Core.Configuration; +@@ -8,7 +9,7 @@ + + namespace NzbDrone.Core.Indexers.Definitions + { +- public class AvistaZ : AvistazBase ++ public class AvistaZ : AvistazBase + { + public override string Name => "AvistaZ"; + public override string[] IndexerUrls => new[] { "https://avistaz.to/" }; +@@ -68,6 +69,29 @@ protected override IndexerCapabilities SetCapabilities() + + public class AvistaZRequestGenerator : AvistazRequestGenerator + { ++ public new AvistaZSettings Settings ++ { ++ get => (AvistaZSettings)base.Settings; ++ set => base.Settings = value; ++ } ++ ++ protected override List> GetBasicSearchParameters(SearchCriteriaBase searchCriteria, string genre = null) ++ { ++ var parameters = base.GetBasicSearchParameters(searchCriteria, genre); ++ ++ foreach (var languageId in Settings.SearchAudioLanguages.Distinct()) ++ { ++ parameters.Add("language[]", languageId.ToString()); ++ } ++ ++ foreach (var languageId in Settings.SearchSubtitleLanguages.Distinct()) ++ { ++ parameters.Add("subtitle[]", languageId.ToString()); ++ } ++ ++ return parameters; ++ } ++ + // AvistaZ has episodes without season. eg Running Man E323 + protected override string GetEpisodeSearchTerm(TvSearchCriteria searchCriteria) + { +diff --git a/src/NzbDrone.Core/Indexers/Definitions/Avistaz/AvistazBase.cs b/src/NzbDrone.Core/Indexers/Definitions/Avistaz/AvistazBase.cs +index b14bac702..1aabd470f 100644 +--- a/src/NzbDrone.Core/Indexers/Definitions/Avistaz/AvistazBase.cs ++++ b/src/NzbDrone.Core/Indexers/Definitions/Avistaz/AvistazBase.cs +@@ -12,7 +12,8 @@ + + namespace NzbDrone.Core.Indexers.Definitions.Avistaz + { +- public abstract class AvistazBase : TorrentIndexerBase ++ public abstract class AvistazBase : TorrentIndexerBase ++ where TSettings : AvistazSettings, new() + { + public override bool SupportsRss => true; + public override bool SupportsSearch => true; +diff --git a/src/NzbDrone.Core/Indexers/Definitions/Avistaz/AvistazSettings.cs b/src/NzbDrone.Core/Indexers/Definitions/Avistaz/AvistazSettings.cs +index bb431c47b..3a535df21 100644 +--- a/src/NzbDrone.Core/Indexers/Definitions/Avistaz/AvistazSettings.cs ++++ b/src/NzbDrone.Core/Indexers/Definitions/Avistaz/AvistazSettings.cs +@@ -1,3 +1,5 @@ ++using System; ++using System.Collections.Generic; + using FluentValidation; + using NzbDrone.Core.Annotations; + using NzbDrone.Core.Indexers.Settings; +@@ -44,4 +46,361 @@ public override NzbDroneValidationResult Validate() + return new NzbDroneValidationResult(Validator.Validate(this)); + } + } ++ ++ public class AvistaZSettings : AvistazSettings ++ { ++ public AvistaZSettings() ++ { ++ SearchAudioLanguages = Array.Empty(); ++ SearchSubtitleLanguages = Array.Empty(); ++ } ++ ++ [FieldDefinition(6, Label = "Search Audio Languages", Type = FieldType.Select, SelectOptions = typeof(AvistaZAudioLanguage), HelpText = "Filter by audio language. The language settings are AND searches, e.g. selecting English audio with Basque and Catalan subtitles will only return results with all three of these.", Advanced = true)] ++ public IEnumerable SearchAudioLanguages { get; set; } ++ ++ [FieldDefinition(7, Label = "Search Subtitle Languages", Type = FieldType.Select, SelectOptions = typeof(AvistaZSubtitleLanguage), HelpText = "Filter by subtitle language. The language settings are AND searches, e.g. selecting English audio with Basque and Catalan subtitles will only return results with all three of these.", Advanced = true)] ++ public IEnumerable SearchSubtitleLanguages { get; set; } ++ } ++ ++ public enum AvistaZAudioLanguage ++ { ++ [FieldOption(Hint = "Arabic")] ++ Arabic = 7, ++ [FieldOption(Hint = "Assamese")] ++ Assamese = 10, ++ [FieldOption(Hint = "Basque")] ++ Basque = 17, ++ [FieldOption(Hint = "Bengali")] ++ Bengali = 19, ++ [FieldOption(Hint = "Bosnian")] ++ Bosnian = 23, ++ [FieldOption(Hint = "Brazilian Portuguese")] ++ BrazilianPortuguese = 189, ++ [FieldOption(Hint = "Burmese")] ++ Burmese = 26, ++ [FieldOption(Hint = "Cantonese")] ++ Cantonese = 27, ++ [FieldOption(Hint = "Catalan")] ++ Catalan = 28, ++ [FieldOption(Hint = "Central Khmer")] ++ CentralKhmer = 29, ++ [FieldOption(Hint = "Chamorro")] ++ Chamorro = 30, ++ [FieldOption(Hint = "Chinese")] ++ Chinese = 33, ++ [FieldOption(Hint = "Croatian")] ++ Croatian = 39, ++ [FieldOption(Hint = "Czech")] ++ Czech = 40, ++ [FieldOption(Hint = "Danish")] ++ Danish = 41, ++ [FieldOption(Hint = "Dutch")] ++ Dutch = 43, ++ [FieldOption(Hint = "Dzongkha")] ++ Dzongkha = 44, ++ [FieldOption(Hint = "English")] ++ English = 45, ++ [FieldOption(Hint = "Esperanto")] ++ Esperanto = 46, ++ [FieldOption(Hint = "Filipino")] ++ Filipino = 188, ++ [FieldOption(Hint = "Finnish")] ++ Finnish = 51, ++ [FieldOption(Hint = "French")] ++ French = 52, ++ [FieldOption(Hint = "German")] ++ German = 58, ++ [FieldOption(Hint = "Greek")] ++ Greek = 59, ++ [FieldOption(Hint = "Gujarati")] ++ Gujarati = 61, ++ [FieldOption(Hint = "Hausa")] ++ Hausa = 63, ++ [FieldOption(Hint = "Hebrew")] ++ Hebrew = 64, ++ [FieldOption(Hint = "Hindi")] ++ Hindi = 66, ++ [FieldOption(Hint = "Hungarian")] ++ Hungarian = 68, ++ [FieldOption(Hint = "Indonesian")] ++ Indonesian = 72, ++ [FieldOption(Hint = "Inuktitut")] ++ Inuktitut = 75, ++ [FieldOption(Hint = "Italian")] ++ Italian = 78, ++ [FieldOption(Hint = "Japanese")] ++ Japanese = 79, ++ [FieldOption(Hint = "Javanese")] ++ Javanese = 80, ++ [FieldOption(Hint = "Kannada")] ++ Kannada = 82, ++ [FieldOption(Hint = "Kashmiri")] ++ Kashmiri = 84, ++ [FieldOption(Hint = "Kazakh")] ++ Kazakh = 85, ++ [FieldOption(Hint = "Kirghiz")] ++ Kirghiz = 88, ++ [FieldOption(Hint = "Korean")] ++ Korean = 91, ++ [FieldOption(Hint = "Lao")] ++ Lao = 94, ++ [FieldOption(Hint = "Malay")] ++ Malay = 104, ++ [FieldOption(Hint = "Malayalam")] ++ Malayalam = 105, ++ [FieldOption(Hint = "Mandarin")] ++ Mandarin = 107, ++ [FieldOption(Hint = "Marathi")] ++ Marathi = 110, ++ [FieldOption(Hint = "Mongolian")] ++ Mongolian = 112, ++ [FieldOption(Hint = "Nepali")] ++ Nepali = 118, ++ [FieldOption(Hint = "Ojibwa")] ++ Ojibwa = 123, ++ [FieldOption(Hint = "Oriya")] ++ Oriya = 124, ++ [FieldOption(Hint = "Pali")] ++ Pali = 127, ++ [FieldOption(Hint = "Panjabi")] ++ Panjabi = 128, ++ [FieldOption(Hint = "Persian")] ++ Persian = 129, ++ [FieldOption(Hint = "Polish")] ++ Polish = 130, ++ [FieldOption(Hint = "Portuguese")] ++ Portuguese = 131, ++ [FieldOption(Hint = "Quechua")] ++ Quechua = 133, ++ [FieldOption(Hint = "Romanian")] ++ Romanian = 134, ++ [FieldOption(Hint = "Russian")] ++ Russian = 137, ++ [FieldOption(Hint = "Sanskrit")] ++ Sanskrit = 140, ++ [FieldOption(Hint = "Sinhala")] ++ Sinhala = 146, ++ [FieldOption(Hint = "Slovak")] ++ Slovak = 147, ++ [FieldOption(Hint = "Spanish")] ++ Spanish = 151, ++ [FieldOption(Hint = "Swedish")] ++ Swedish = 155, ++ [FieldOption(Hint = "Tagalog")] ++ Tagalog = 156, ++ [FieldOption(Hint = "Tamil")] ++ Tamil = 159, ++ [FieldOption(Hint = "Telugu")] ++ Telugu = 161, ++ [FieldOption(Hint = "Thai")] ++ Thai = 162, ++ [FieldOption(Hint = "Tibetan")] ++ Tibetan = 163, ++ [FieldOption(Hint = "Turkish")] ++ Turkish = 168, ++ [FieldOption(Hint = "Uighur")] ++ Uighur = 171, ++ [FieldOption(Hint = "Ukrainian")] ++ Ukrainian = 172, ++ [FieldOption(Hint = "Urdu")] ++ Urdu = 173, ++ [FieldOption(Hint = "Vietnamese")] ++ Vietnamese = 176, ++ [FieldOption(Hint = "Xhosa")] ++ Xhosa = 182, ++ } ++ ++ public enum AvistaZSubtitleLanguage ++ { ++ [FieldOption(Hint = "Abkhazian")] ++ Abkhazian = 1, ++ [FieldOption(Hint = "Afar")] ++ Afar = 2, ++ [FieldOption(Hint = "Afrikaans")] ++ Afrikaans = 3, ++ [FieldOption(Hint = "Albanian")] ++ Albanian = 5, ++ [FieldOption(Hint = "Amharic")] ++ Amharic = 6, ++ [FieldOption(Hint = "Arabic")] ++ Arabic = 7, ++ [FieldOption(Hint = "Basque")] ++ Basque = 17, ++ [FieldOption(Hint = "Belarusian")] ++ Belarusian = 18, ++ [FieldOption(Hint = "Bengali")] ++ Bengali = 19, ++ [FieldOption(Hint = "Norwegian Bokmål")] ++ NorwegianBokmal = 22, ++ [FieldOption(Hint = "Bosnian")] ++ Bosnian = 23, ++ [FieldOption(Hint = "Bulgarian")] ++ Bulgarian = 25, ++ [FieldOption(Hint = "Brazilian Portuguese")] ++ BrazilianPortuguese = 189, ++ [FieldOption(Hint = "Burmese")] ++ Burmese = 26, ++ [FieldOption(Hint = "Cantonese")] ++ Cantonese = 27, ++ [FieldOption(Hint = "Catalan")] ++ Catalan = 28, ++ [FieldOption(Hint = "Central Khmer")] ++ CentralKhmer = 29, ++ [FieldOption(Hint = "Chichewa")] ++ Chichewa = 32, ++ [FieldOption(Hint = "Chinese")] ++ Chinese = 33, ++ [FieldOption(Hint = "Corsican")] ++ Corsican = 37, ++ [FieldOption(Hint = "Croatian")] ++ Croatian = 39, ++ [FieldOption(Hint = "Czech")] ++ Czech = 40, ++ [FieldOption(Hint = "Danish")] ++ Danish = 41, ++ [FieldOption(Hint = "Dutch")] ++ Dutch = 43, ++ [FieldOption(Hint = "English")] ++ English = 45, ++ [FieldOption(Hint = "Esperanto")] ++ Esperanto = 46, ++ [FieldOption(Hint = "Estonian")] ++ Estonian = 47, ++ [FieldOption(Hint = "Fijian")] ++ Fijian = 50, ++ [FieldOption(Hint = "Filipino")] ++ Filipino = 188, ++ [FieldOption(Hint = "Finnish")] ++ Finnish = 51, ++ [FieldOption(Hint = "French")] ++ French = 52, ++ [FieldOption(Hint = "Galician")] ++ Galician = 55, ++ [FieldOption(Hint = "Georgian")] ++ Georgian = 57, ++ [FieldOption(Hint = "German")] ++ German = 58, ++ [FieldOption(Hint = "Greek")] ++ Greek = 59, ++ [FieldOption(Hint = "Gujarati")] ++ Gujarati = 61, ++ [FieldOption(Hint = "Haitian")] ++ Haitian = 62, ++ [FieldOption(Hint = "Hebrew")] ++ Hebrew = 64, ++ [FieldOption(Hint = "Hindi")] ++ Hindi = 66, ++ [FieldOption(Hint = "Hungarian")] ++ Hungarian = 68, ++ [FieldOption(Hint = "Icelandic")] ++ Icelandic = 69, ++ [FieldOption(Hint = "Indonesian")] ++ Indonesian = 72, ++ [FieldOption(Hint = "Irish")] ++ Irish = 77, ++ [FieldOption(Hint = "Italian")] ++ Italian = 78, ++ [FieldOption(Hint = "Japanese")] ++ Japanese = 79, ++ [FieldOption(Hint = "Javanese")] ++ Javanese = 80, ++ [FieldOption(Hint = "Kannada")] ++ Kannada = 82, ++ [FieldOption(Hint = "Kazakh")] ++ Kazakh = 85, ++ [FieldOption(Hint = "Korean")] ++ Korean = 91, ++ [FieldOption(Hint = "Kurdish")] ++ Kurdish = 93, ++ [FieldOption(Hint = "Lao")] ++ Lao = 94, ++ [FieldOption(Hint = "Latin")] ++ Latin = 95, ++ [FieldOption(Hint = "Latvian")] ++ Latvian = 96, ++ [FieldOption(Hint = "Lithuanian")] ++ Lithuanian = 99, ++ [FieldOption(Hint = "Macedonian")] ++ Macedonian = 102, ++ [FieldOption(Hint = "Malagasy")] ++ Malagasy = 103, ++ [FieldOption(Hint = "Malay")] ++ Malay = 104, ++ [FieldOption(Hint = "Malayalam")] ++ Malayalam = 105, ++ [FieldOption(Hint = "Mandarin")] ++ Mandarin = 107, ++ [FieldOption(Hint = "Maori")] ++ Maori = 109, ++ [FieldOption(Hint = "Marathi")] ++ Marathi = 110, ++ [FieldOption(Hint = "Mongolian")] ++ Mongolian = 112, ++ [FieldOption(Hint = "Nepali")] ++ Nepali = 118, ++ [FieldOption(Hint = "Norwegian")] ++ Norwegian = 120, ++ [FieldOption(Hint = "Oriya")] ++ Oriya = 124, ++ [FieldOption(Hint = "Panjabi")] ++ Panjabi = 128, ++ [FieldOption(Hint = "Persian")] ++ Persian = 129, ++ [FieldOption(Hint = "Polish")] ++ Polish = 130, ++ [FieldOption(Hint = "Portuguese")] ++ Portuguese = 131, ++ [FieldOption(Hint = "Romanian")] ++ Romanian = 134, ++ [FieldOption(Hint = "Romansh")] ++ Romansh = 135, ++ [FieldOption(Hint = "Russian")] ++ Russian = 137, ++ [FieldOption(Hint = "Sardinian")] ++ Sardinian = 141, ++ [FieldOption(Hint = "Serbian")] ++ Serbian = 142, ++ [FieldOption(Hint = "Sinhala")] ++ Sinhala = 146, ++ [FieldOption(Hint = "Slovak")] ++ Slovak = 147, ++ [FieldOption(Hint = "Slovenian")] ++ Slovenian = 148, ++ [FieldOption(Hint = "Somali")] ++ Somali = 149, ++ [FieldOption(Hint = "Spanish")] ++ Spanish = 151, ++ [FieldOption(Hint = "Sundanese")] ++ Sundanese = 152, ++ [FieldOption(Hint = "Swahili")] ++ Swahili = 153, ++ [FieldOption(Hint = "Swedish")] ++ Swedish = 155, ++ [FieldOption(Hint = "Tagalog")] ++ Tagalog = 156, ++ [FieldOption(Hint = "Tamil")] ++ Tamil = 159, ++ [FieldOption(Hint = "Telugu")] ++ Telugu = 161, ++ [FieldOption(Hint = "Thai")] ++ Thai = 162, ++ [FieldOption(Hint = "Turkish")] ++ Turkish = 168, ++ [FieldOption(Hint = "Twi")] ++ Twi = 170, ++ [FieldOption(Hint = "Ukrainian")] ++ Ukrainian = 172, ++ [FieldOption(Hint = "Urdu")] ++ Urdu = 173, ++ [FieldOption(Hint = "Venda")] ++ Venda = 175, ++ [FieldOption(Hint = "Vietnamese")] ++ Vietnamese = 176, ++ [FieldOption(Hint = "Volapük")] ++ Volapuk = 177, ++ [FieldOption(Hint = "Welsh")] ++ Welsh = 179, ++ [FieldOption(Hint = "Wolof")] ++ Wolof = 181, ++ } + } +diff --git a/src/NzbDrone.Core/Indexers/Definitions/Avistaz/CinemaZSettings.cs b/src/NzbDrone.Core/Indexers/Definitions/Avistaz/CinemaZSettings.cs +new file mode 100644 +index 000000000..fa133a0ed +--- /dev/null ++++ b/src/NzbDrone.Core/Indexers/Definitions/Avistaz/CinemaZSettings.cs +@@ -0,0 +1,21 @@ ++using System; ++using System.Collections.Generic; ++using NzbDrone.Core.Annotations; ++ ++namespace NzbDrone.Core.Indexers.Definitions.Avistaz ++{ ++ public class CinemaZSettings : AvistazSettings ++ { ++ public CinemaZSettings() ++ { ++ SearchAudioLanguages = Array.Empty(); ++ SearchSubtitleLanguages = Array.Empty(); ++ } ++ ++ [FieldDefinition(6, Label = "Search Audio Languages", Type = FieldType.Select, SelectOptions = typeof(AvistaZAudioLanguage), HelpText = "Filter by audio language. The language settings are AND searches, e.g. selecting English audio with Basque and Catalan subtitles will only return results with all three of these.", Advanced = true)] ++ public IEnumerable SearchAudioLanguages { get; set; } ++ ++ [FieldDefinition(7, Label = "Search Subtitle Languages", Type = FieldType.Select, SelectOptions = typeof(AvistaZSubtitleLanguage), HelpText = "Filter by subtitle language. The language settings are AND searches, e.g. selecting English audio with Basque and Catalan subtitles will only return results with all three of these.", Advanced = true)] ++ public IEnumerable SearchSubtitleLanguages { get; set; } ++ } ++} +diff --git a/src/NzbDrone.Core/Indexers/Definitions/Avistaz/PrivateHDSettings.cs b/src/NzbDrone.Core/Indexers/Definitions/Avistaz/PrivateHDSettings.cs +new file mode 100644 +index 000000000..31967b586 +--- /dev/null ++++ b/src/NzbDrone.Core/Indexers/Definitions/Avistaz/PrivateHDSettings.cs +@@ -0,0 +1,21 @@ ++using System; ++using System.Collections.Generic; ++using NzbDrone.Core.Annotations; ++ ++namespace NzbDrone.Core.Indexers.Definitions.Avistaz ++{ ++ public class PrivateHDSettings : AvistazSettings ++ { ++ public PrivateHDSettings() ++ { ++ SearchAudioLanguages = Array.Empty(); ++ SearchSubtitleLanguages = Array.Empty(); ++ } ++ ++ [FieldDefinition(6, Label = "Search Audio Languages", Type = FieldType.Select, SelectOptions = typeof(AvistaZAudioLanguage), HelpText = "Filter by audio language. The language settings are AND searches, e.g. selecting English audio with Basque and Catalan subtitles will only return results with all three of these.", Advanced = true)] ++ public IEnumerable SearchAudioLanguages { get; set; } ++ ++ [FieldDefinition(7, Label = "Search Subtitle Languages", Type = FieldType.Select, SelectOptions = typeof(AvistaZSubtitleLanguage), HelpText = "Filter by subtitle language. The language settings are AND searches, e.g. selecting English audio with Basque and Catalan subtitles will only return results with all three of these.", Advanced = true)] ++ public IEnumerable SearchSubtitleLanguages { get; set; } ++ } ++} +diff --git a/src/NzbDrone.Core/Indexers/Definitions/CinemaZ.cs b/src/NzbDrone.Core/Indexers/Definitions/CinemaZ.cs +index 3f0fdd913..e3318c195 100644 +--- a/src/NzbDrone.Core/Indexers/Definitions/CinemaZ.cs ++++ b/src/NzbDrone.Core/Indexers/Definitions/CinemaZ.cs +@@ -1,12 +1,14 @@ + using System.Collections.Generic; ++using System.Linq; + using NLog; + using NzbDrone.Core.Configuration; + using NzbDrone.Core.Indexers.Definitions.Avistaz; ++using NzbDrone.Core.IndexerSearch.Definitions; + using NzbDrone.Core.Messaging.Events; + + namespace NzbDrone.Core.Indexers.Definitions + { +- public class CinemaZ : AvistazBase ++ public class CinemaZ : AvistazBase + { + public override string Name => "CinemaZ"; + public override string[] IndexerUrls => new[] { "https://cinemaz.to/" }; +@@ -23,6 +25,18 @@ public CinemaZ(IIndexerRepository indexerRepository, + { + } + ++ public override IIndexerRequestGenerator GetRequestGenerator() ++ { ++ return new CinemaZRequestGenerator ++ { ++ Settings = Settings, ++ Capabilities = Capabilities, ++ PageSize = PageSize, ++ HttpClient = _httpClient, ++ Logger = _logger ++ }; ++ } ++ + protected override IndexerCapabilities SetCapabilities() + { + var caps = new IndexerCapabilities +@@ -51,4 +65,30 @@ protected override IndexerCapabilities SetCapabilities() + return caps; + } + } ++ ++ public class CinemaZRequestGenerator : AvistazRequestGenerator ++ { ++ public new CinemaZSettings Settings ++ { ++ get => (CinemaZSettings)base.Settings; ++ set => base.Settings = value; ++ } ++ ++ protected override List> GetBasicSearchParameters(SearchCriteriaBase searchCriteria, string genre = null) ++ { ++ var parameters = base.GetBasicSearchParameters(searchCriteria, genre); ++ ++ foreach (var languageId in Settings.SearchAudioLanguages.Distinct()) ++ { ++ parameters.Add("language[]", languageId.ToString()); ++ } ++ ++ foreach (var languageId in Settings.SearchSubtitleLanguages.Distinct()) ++ { ++ parameters.Add("subtitle[]", languageId.ToString()); ++ } ++ ++ return parameters; ++ } ++ } + } +diff --git a/src/NzbDrone.Core/Indexers/Definitions/ExoticaZ.cs b/src/NzbDrone.Core/Indexers/Definitions/ExoticaZ.cs +index 954d2442c..41202ab21 100644 +--- a/src/NzbDrone.Core/Indexers/Definitions/ExoticaZ.cs ++++ b/src/NzbDrone.Core/Indexers/Definitions/ExoticaZ.cs +@@ -7,7 +7,7 @@ + + namespace NzbDrone.Core.Indexers.Definitions + { +- public class ExoticaZ : AvistazBase ++ public class ExoticaZ : AvistazBase + { + public override string Name => "ExoticaZ"; + public override string[] IndexerUrls => new[] { "https://exoticaz.to/" }; +diff --git a/src/NzbDrone.Core/Indexers/Definitions/PrivateHD.cs b/src/NzbDrone.Core/Indexers/Definitions/PrivateHD.cs +index 21fc76ede..df33867c1 100644 +--- a/src/NzbDrone.Core/Indexers/Definitions/PrivateHD.cs ++++ b/src/NzbDrone.Core/Indexers/Definitions/PrivateHD.cs +@@ -1,12 +1,14 @@ + using System.Collections.Generic; ++using System.Linq; + using NLog; + using NzbDrone.Core.Configuration; + using NzbDrone.Core.Indexers.Definitions.Avistaz; ++using NzbDrone.Core.IndexerSearch.Definitions; + using NzbDrone.Core.Messaging.Events; + + namespace NzbDrone.Core.Indexers.Definitions + { +- public class PrivateHD : AvistazBase ++ public class PrivateHD : AvistazBase + { + public override string Name => "PrivateHD"; + public override string[] IndexerUrls => new[] { "https://privatehd.to/" }; +@@ -23,6 +25,18 @@ public PrivateHD(IIndexerRepository indexerRepository, + { + } + ++ public override IIndexerRequestGenerator GetRequestGenerator() ++ { ++ return new PrivateHDRequestGenerator ++ { ++ Settings = Settings, ++ Capabilities = Capabilities, ++ PageSize = PageSize, ++ HttpClient = _httpClient, ++ Logger = _logger ++ }; ++ } ++ + protected override IndexerCapabilities SetCapabilities() + { + var caps = new IndexerCapabilities +@@ -51,4 +65,30 @@ protected override IndexerCapabilities SetCapabilities() + return caps; + } + } ++ ++ public class PrivateHDRequestGenerator : AvistazRequestGenerator ++ { ++ public new PrivateHDSettings Settings ++ { ++ get => (PrivateHDSettings)base.Settings; ++ set => base.Settings = value; ++ } ++ ++ protected override List> GetBasicSearchParameters(SearchCriteriaBase searchCriteria, string genre = null) ++ { ++ var parameters = base.GetBasicSearchParameters(searchCriteria, genre); ++ ++ foreach (var languageId in Settings.SearchAudioLanguages.Distinct()) ++ { ++ parameters.Add("language[]", languageId.ToString()); ++ } ++ ++ foreach (var languageId in Settings.SearchSubtitleLanguages.Distinct()) ++ { ++ parameters.Add("subtitle[]", languageId.ToString()); ++ } ++ ++ return parameters; ++ } ++ } + } +-- +2.50.1 +