New Upstream Release - kodi-pvr-zattoo

Ready changes

Summary

Merged new upstream version: 20.3.13+ds (was: 20.3.3+ds).

Resulting package

Built on 2023-07-25T12:15 (took 5m39s)

The resulting binary packages can be installed (if you have the apt repository enabled) by running one of:

apt install -t fresh-releases kodi-pvr-zattoo-dbgsymapt install -t fresh-releases kodi-pvr-zattoo

Lintian Result

Diff

diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
new file mode 100644
index 0000000..8e5ad77
--- /dev/null
+++ b/.github/workflows/build.yml
@@ -0,0 +1,58 @@
+name: Build and run tests
+on: [push, pull_request]
+env:
+  app_id: pvr.zattoo
+
+jobs:
+  build:
+    runs-on: ${{ matrix.os }}
+    strategy:
+      fail-fast: false
+      matrix:
+        include:
+        - name: "Debian package test"
+          os: ubuntu-22.04
+          CC: gcc
+          CXX: g++
+          DEBIAN_BUILD: true
+        - os: ubuntu-22.04
+          CC: gcc
+          CXX: g++
+    steps:
+    - name: Install needed dependencies
+      env:
+        DEBIAN_BUILD: ${{ matrix.DEBIAN_BUILD }}
+      run: |
+        if [[ $DEBIAN_BUILD == true ]]; then sudo add-apt-repository -y ppa:team-xbmc/xbmc-nightly; fi
+        if [[ $DEBIAN_BUILD == true ]]; then sudo apt-get update; fi
+        if [[ $DEBIAN_BUILD == true ]]; then sudo apt-get install fakeroot; fi
+        if [[ $RUNNER_OS == 'macOS' ]]; then brew install automake; fi
+    - name: Checkout Kodi repo
+      uses: actions/checkout@v2
+      with:
+        repository: xbmc/xbmc
+        ref: master
+        path: xbmc
+    - name: Checkout pvr.zattoo repo
+      uses: actions/checkout@v2
+      with:
+        path: ${{ env.app_id }}
+    - name: Configure
+      env:
+        CC: ${{ matrix.CC }}
+        CXX: ${{ matrix.CXX }}
+        DEBIAN_BUILD: ${{ matrix.DEBIAN_BUILD }}
+      run: |
+        if [[ $DEBIAN_BUILD != true ]]; then cd ${app_id} && mkdir -p build && cd build; fi
+        if [[ $DEBIAN_BUILD != true ]]; then cmake -DADDONS_TO_BUILD=${app_id} -DADDON_SRC_PREFIX=${{ github.workspace }} -DCMAKE_BUILD_TYPE=Debug -DCMAKE_INSTALL_PREFIX=${{ github.workspace }}/xbmc/addons -DPACKAGE_ZIP=1 ${{ github.workspace }}/xbmc/cmake/addons; fi
+        if [[ $DEBIAN_BUILD == true ]]; then wget https://raw.githubusercontent.com/xbmc/xbmc/master/xbmc/addons/kodi-dev-kit/tools/debian-addon-package-test.sh && chmod +x ./debian-addon-package-test.sh; fi
+        if [[ $DEBIAN_BUILD == true ]]; then sudo apt-get build-dep ${{ github.workspace }}/${app_id}; fi
+    - name: Build
+      env:
+        CC: ${{ matrix.CC }}
+        CXX: ${{ matrix.CXX }}
+        DEBIAN_BUILD: ${{ matrix.DEBIAN_BUILD }}
+      run: |
+        if [[ $DEBIAN_BUILD != true ]]; then cd ${app_id}/build; fi
+        if [[ $DEBIAN_BUILD != true ]]; then make; fi
+        if [[ $DEBIAN_BUILD == true ]]; then ./debian-addon-package-test.sh ${{ github.workspace }}/${app_id}; fi
diff --git a/.github/workflows/changelog-and-release.yml b/.github/workflows/changelog-and-release.yml
new file mode 100644
index 0000000..e3d3b4e
--- /dev/null
+++ b/.github/workflows/changelog-and-release.yml
@@ -0,0 +1,149 @@
+name: Changelog and Release
+# Update the changelog and news(optionally), bump the version, and create a release
+#
+# The release is created on the given branch, release and tag name format will be <version>-<branch> and
+# the body of the release will be created from the changelog.txt or news element in the addon.xml.in
+#
+# options:
+# - version_type: 'minor' / 'micro' # whether to do a minor or micro version bump
+# - changelog_text: string to add to the changelog and news
+# - update_news: 'true' / 'false' # whether to update the news in the addon.xml.in
+# - add_date: 'true' / 'false' # Add date to version number in changelog and news. ie. v1.0.1 (2021-7-17)
+
+on:
+  workflow_dispatch:
+    inputs:
+      version_type:
+        description: 'Create a ''minor'' or ''micro'' release?'
+        required: true
+        default: 'minor'
+      changelog_text:
+        description: 'Input the changes you''d like to add to the changelogs. Your text should be encapsulated in "''s with line feeds represented by literal \n''s. ie. "This is the first change\nThis is the second change"'
+        required: true
+        default: ''
+      update_news:
+        description: 'Update news in addon.xml.in? [true|false]'
+        required: true
+        default: 'true'
+      add_date:
+        description: 'Add date to version number in changelog and news. ie. "v1.0.1 (2021-7-17)" [true|false]'
+        required: true
+        default: 'false'
+
+jobs:
+  default:
+    runs-on: ubuntu-latest
+    name: Changelog and Release
+
+    steps:
+
+      # Checkout the current repository into a directory (repositories name)
+      - name: Checkout Repository
+        uses: actions/checkout@v2
+        with:
+          fetch-depth: 0
+          path: ${{ github.event.repository.name }}
+
+      # Checkout the required scripts from kodi-pvr/pvr-scripts into the 'scripts' directory
+      - name: Checkout Scripts
+        uses: actions/checkout@v2
+        with:
+          fetch-depth: 0
+          repository: kodi-pvr/pvr-scripts
+          path: scripts
+
+      # Install all dependencies required by the following steps
+      # - libxml2-utils, xmlstarlet: reading news and version from addon.xml.in
+      - name: Install dependencies
+        run: |
+          sudo apt-get install libxml2-utils xmlstarlet
+
+      # Setup python version 3.9
+      - name: Set up Python
+        uses: actions/setup-python@v2
+        with:
+          python-version: '3.9'
+
+      # Run the python script to increment the version, changelog and news
+      - name: Increment version and update changelogs
+        run: |
+          arguments=
+          if [[ ${{ github.event.inputs.update_news }} == true ]] ;
+          then
+            arguments=$(echo $arguments && echo --update-news)
+          fi
+          if [[ ${{ github.event.inputs.add_date }} == true ]] ;
+          then
+            arguments=$(echo $arguments && echo --add-date)
+          fi
+          python3 ../scripts/changelog_and_release.py ${{ github.event.inputs.version_type }} ${{ github.event.inputs.changelog_text }} $arguments
+        working-directory: ${{ github.event.repository.name }}
+
+      # Create the variables required by the following steps
+      # - steps.required-variables.outputs.changes: latest entry in the changelog.txt (if exists), or addon.xml.in news element
+      # - steps.required-variables.outputs.version: version element from addon.xml.in
+      # - steps.required-variables.outputs.branch: branch of the triggering ref
+      # - steps.required-variables.outputs.today: today's date in format '%Y-%m-%d'
+      - name: Get required variables
+        id: required-variables
+        run: |
+          changes=$(cat "$(find . -name changelog.txt)" | awk -v RS= 'NR==1')
+          if [ -z "$changes" ] ;
+          then
+            changes=$(xmlstarlet fo -R "$(find . -name addon.xml.in)" | xmlstarlet sel -t -v 'string(/addon/extension/news)' | awk -v RS= 'NR==1')
+          fi
+          changes="${changes//'%'/'%25'}"
+          changes="${changes//$'\n'/'%0A'}"
+          changes="${changes//$'\r'/'%0D'}"
+          changes="${changes//$'\\n'/'%0A'}"
+          changes="${changes//$'\\r'/'%0D'}"
+          echo ::set-output name=changes::$changes
+          version=$(xmlstarlet fo -R "$(find . -name addon.xml.in)" | xmlstarlet sel -t -v 'string(/addon/@version)')
+          echo ::set-output name=version::$version
+          branch=$(echo ${GITHUB_REF#refs/heads/})
+          echo ::set-output name=branch::$branch
+          echo ::set-output name=today::$(date +'%Y-%m-%d')
+        working-directory: ${{ github.event.repository.name }}
+
+      # Create a commit of the incremented version and changelog, news changes
+      # Commit message (add_date=false): changelog and version v{steps.required-variables.outputs.version}
+      # Commit message (add_date=true): changelog and version v{steps.required-variables.outputs.version} ({steps.required-variables.outputs.today})
+      - name: Commit changes
+        run: |
+          git config --local user.email "41898282+github-actions[bot]@users.noreply.github.com"
+          git config --local user.name "github-actions[bot]"
+          commit_message="changelog and version v${{ steps.required-variables.outputs.version }}"
+          if [[ ${{ github.event.inputs.add_date }} == true ]] ;
+          then
+            commit_message="$commit_message (${{ steps.required-variables.outputs.today }})"
+          fi
+          git commit -m "$commit_message" -a
+        working-directory: ${{ github.event.repository.name }}
+
+      # Push the commit(s) created above to the triggering branch
+      - name: Push changes
+        uses: ad-m/github-push-action@master
+        with:
+          branch: ${{ github.ref }}
+          directory: ${{ github.event.repository.name }}
+
+      # Sleep for 60 seconds to allow for any delays in the push
+      - name: Sleep for 60 seconds
+        run: sleep 60s
+        shell: bash
+
+      # Create a release at {steps.required-variables.outputs.branch}
+      # - tag and release name format: {steps.required-variables.outputs.version}-{steps.required-variables.outputs.branch} ie. 1.0.0-Matrix
+      # - release body: {steps.required-variables.outputs.changes}
+      - name: Create Release
+        id: create-release
+        uses: actions/create-release@v1
+        env:
+          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+        with:
+          tag_name: ${{ steps.required-variables.outputs.version }}-${{ steps.required-variables.outputs.branch }}
+          release_name: ${{ steps.required-variables.outputs.version }}-${{ steps.required-variables.outputs.branch }}
+          body: ${{ steps.required-variables.outputs.changes }}
+          draft: false
+          prerelease: false
+          commitish: ${{ steps.required-variables.outputs.branch }}
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
new file mode 100644
index 0000000..f413289
--- /dev/null
+++ b/.github/workflows/release.yml
@@ -0,0 +1,66 @@
+name: Make Release
+# Create a release on the given branch
+# Release and tag name format will be <version>-<branch>
+# The body of the release will be created from the changelog.txt or news element in the addon.xml.in
+
+on: workflow_dispatch
+
+jobs:
+  default:
+    runs-on: ubuntu-latest
+    name: Make Release
+
+    steps:
+
+      # Checkout the current repository into a directory (repositories name)
+      - name: Checkout Repository
+        uses: actions/checkout@v2
+        with:
+          fetch-depth: 0
+          path: ${{ github.event.repository.name }}
+
+      # Install all dependencies required by the following steps
+      # - libxml2-utils, xmlstarlet: reading news and version from addon.xml.in
+      - name: Install dependencies
+        run: |
+          sudo apt-get install libxml2-utils xmlstarlet
+
+      # Create the variables required by the following steps
+      # - steps.required-variables.outputs.changes: latest entry in the changelog.txt (if exists), or addon.xml.in news element
+      # - steps.required-variables.outputs.version: version element from addon.xml.in
+      # - steps.required-variables.outputs.branch: branch of the triggering ref
+      - name: Get required variables
+        id: required-variables
+        run: |
+          changes=$(cat "$(find . -name changelog.txt)" | awk -v RS= 'NR==1')
+          if [ -z "$changes" ] ;
+          then
+            changes=$(xmlstarlet fo -R "$(find . -name addon.xml.in)" | xmlstarlet sel -t -v 'string(/addon/extension/news)' | awk -v RS= 'NR==1')
+          fi
+          changes="${changes//'%'/'%25'}"
+          changes="${changes//$'\n'/'%0A'}"
+          changes="${changes//$'\r'/'%0D'}"
+          changes="${changes//$'\\n'/'%0A'}"
+          changes="${changes//$'\\r'/'%0D'}"
+          echo ::set-output name=changes::$changes
+          version=$(xmlstarlet fo -R "$(find . -name addon.xml.in)" | xmlstarlet sel -t -v 'string(/addon/@version)')
+          echo ::set-output name=version::$version
+          branch=$(echo ${GITHUB_REF#refs/heads/})
+          echo ::set-output name=branch::$branch
+        working-directory: ${{ github.event.repository.name }}
+
+      # Create a release at {steps.required-variables.outputs.branch}
+      # - tag and release name format: {steps.required-variables.outputs.version}-{steps.required-variables.outputs.branch} ie. 1.0.0-Matrix
+      # - release body: {steps.required-variables.outputs.changes}
+      - name: Create Release
+        id: create-release
+        uses: actions/create-release@v1
+        env:
+          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+        with:
+          tag_name: ${{ steps.required-variables.outputs.version }}-${{ steps.required-variables.outputs.branch }}
+          release_name: ${{ steps.required-variables.outputs.version }}-${{ steps.required-variables.outputs.branch }}
+          body: ${{ steps.required-variables.outputs.changes }}
+          draft: false
+          prerelease: false
+          commitish: ${{ steps.required-variables.outputs.branch }}
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..b8b8fd2
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,50 @@
+# build artifacts
+build/
+pvr.*/addon.xml
+
+# Debian build files
+debian/changelog
+debian/files
+debian/*.log
+debian/*.substvars
+debian/.debhelper/
+debian/tmp/
+debian/kodi-pvr-*/
+obj-x86_64-linux-gnu/
+
+# commonly used editors
+# vim
+*.swp
+
+# Eclipse
+*.project
+*.cproject
+.classpath
+*.sublime-*
+.settings/
+
+# KDevelop 4
+*.kdev4
+
+# gedit
+*~
+
+# CLion
+/.idea
+
+# clion
+.idea/
+
+# to prevent add after a "git format-patch VALUE" and "git add ." call
+/*.patch
+
+# Visual Studio Code
+.vscode
+
+# to prevent add if project code opened by Visual Studio over CMake file
+.vs/
+
+# General MacOS
+.DS_Store
+.AppleDouble
+.LSOverride
diff --git a/debian/changelog b/debian/changelog
index a58495a..7b96042 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,9 @@
+kodi-pvr-zattoo (20.3.13+ds-1) UNRELEASED; urgency=low
+
+  * New upstream release.
+
+ -- Debian Janitor <janitor@jelmer.uk>  Tue, 25 Jul 2023 12:09:59 -0000
+
 kodi-pvr-zattoo (20.3.3+ds-1) unstable; urgency=medium
 
   * New upstream version 20.3.3+ds
diff --git a/pvr.zattoo/addon.xml.in b/pvr.zattoo/addon.xml.in
index d263479..a4663b4 100644
--- a/pvr.zattoo/addon.xml.in
+++ b/pvr.zattoo/addon.xml.in
@@ -1,6 +1,6 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <addon id="pvr.zattoo"
-       version="20.3.3"
+       version="20.3.13"
        name="Zattoo PVR Client"
        provider-name="trummerjo,rbuehlma">
   <requires>
diff --git a/pvr.zattoo/changelog.txt b/pvr.zattoo/changelog.txt
index 6cbbd30..cc1d255 100644
--- a/pvr.zattoo/changelog.txt
+++ b/pvr.zattoo/changelog.txt
@@ -1,3 +1,25 @@
+v20.3.13
+- Honor selection of no DRM in any case
+v20.3.12
+- Allow setting DRM level or disable DRM completely
+v20.3.11
+- Use widevine only if required by selected quality
+v20.3.10
+- Fix playing recordings for RTL group
+- Remove option to select stream type. Always use dash (widevine)
+- Add option to force enabling DRM (widevine) L2 for Linux systems
+v20.3.9
+- Fix fetching SD stream by not setting empty quality parameter
+v20.3.8
+- Adapt session handling
+v20.3.7
+- Use dash without widevine if no DRM is required
+v20.3.6
+- Linux: Use channel quality without DRM requirement
+v20.3.5
+- Show recordings of removed channels
+v20.3.4
+- Show error if login fails
 v20.3.3
 - Fix segfault during addon creation
 v20.3.2
diff --git a/pvr.zattoo/resources/language/resource.language.de_de/strings.po b/pvr.zattoo/resources/language/resource.language.de_de/strings.po
index 4c80993..cee4078 100644
--- a/pvr.zattoo/resources/language/resource.language.de_de/strings.po
+++ b/pvr.zattoo/resources/language/resource.language.de_de/strings.po
@@ -48,24 +48,6 @@ msgctxt "#30005"
 msgid "To select wanted encrypted stream type."
 msgstr "Um den gewünschten verschlüsselten Stream-Typ auszuwählen."
 
-#. Selection value for setting defined with id #30004
-#: pvr.zattoo/resources/settings.xml
-msgctxt "#30006"
-msgid "dash"
-msgstr "dash"
-
-#. Selection value for setting defined with id #30004
-#: pvr.zattoo/resources/settings.xml
-msgctxt "#30007"
-msgid "hls"
-msgstr "hls"
-
-#. Selection value for setting defined with id #30004
-#: pvr.zattoo/resources/settings.xml
-msgctxt "#30008"
-msgid "dash_widevine"
-msgstr "dash_widevine"
-
 #. Text setting to set used parental pin
 #: pvr.zattoo/resources/settings.xml
 msgctxt "#30009"
@@ -96,6 +78,12 @@ msgctxt "#30015"
 msgid "General"
 msgstr "Allgemein"
 
+#. Boolean setting to enforce widevine for recordings
+#: pvr.zattoo/resources/settings.xml
+msgctxt "#30016"
+msgid "Widevine L2 forcieren"
+msgstr ""
+
 #. Selection list on settings to get wanted stream provider
 #: pvr.zattoo/resources/settings.xml
 msgctxt "#30098"
@@ -221,3 +209,44 @@ msgctxt "#30201"
 msgid "Login failed!"
 msgstr "Anmeldung fehlgeschlagen!"
 
+#. Selection value for setting defined with id #30398
+#: pvr.zattoo/resources/settings.xml
+msgctxt "#30300"
+msgid "Auto"
+msgstr ""
+
+#. Selection value for setting defined with id #30398
+#: pvr.zattoo/resources/settings.xml
+msgctxt "#30301"
+msgid "1"
+msgstr ""
+
+#. Selection value for setting defined with id #30398
+#: pvr.zattoo/resources/settings.xml
+msgctxt "#30302"
+msgid "2"
+msgstr ""
+
+#. Selection value for setting defined with id #30398
+#: pvr.zattoo/resources/settings.xml
+msgctxt "#30303"
+msgid "3"
+msgstr ""
+
+#. Selection value for setting defined with id #30398
+#: pvr.zattoo/resources/settings.xml
+msgctxt "#30304"
+msgid "DRM deaktivieren"
+msgstr ""
+
+#. Selection list on settings to get wanted stream provider
+#: pvr.zattoo/resources/settings.xml
+msgctxt "#30398"
+msgid "DRM Level"
+msgstr ""
+
+#. Help text to setting #30098
+#: pvr.zattoo/resources/settings.xml
+msgctxt "#30399"
+msgid "Unterstütztes DRM level. Auto verwendet L3 für Linux, sonst L2."
+msgstr ""
diff --git a/pvr.zattoo/resources/language/resource.language.en_gb/strings.po b/pvr.zattoo/resources/language/resource.language.en_gb/strings.po
index 011e5c4..761f9a4 100644
--- a/pvr.zattoo/resources/language/resource.language.en_gb/strings.po
+++ b/pvr.zattoo/resources/language/resource.language.en_gb/strings.po
@@ -48,24 +48,6 @@ msgctxt "#30005"
 msgid "To select wanted encrypted stream type."
 msgstr ""
 
-#. Selection value for setting defined with id #30004
-#: pvr.zattoo/resources/settings.xml
-msgctxt "#30006"
-msgid "dash"
-msgstr ""
-
-#. Selection value for setting defined with id #30004
-#: pvr.zattoo/resources/settings.xml
-msgctxt "#30007"
-msgid "hls"
-msgstr ""
-
-#. Selection value for setting defined with id #30004
-#: pvr.zattoo/resources/settings.xml
-msgctxt "#30008"
-msgid "dash_widevine"
-msgstr ""
-
 #. Text setting to set used parental pin
 #: pvr.zattoo/resources/settings.xml
 msgctxt "#30009"
@@ -100,6 +82,12 @@ msgctxt "#30015"
 msgid "General"
 msgstr ""
 
+#. Boolean setting to enforce widevine l2
+#: pvr.zattoo/resources/settings.xml
+msgctxt "#30016"
+msgid "Enforce Widevine L2"
+msgstr ""
+
 #. Selection list on settings to get wanted stream provider
 #: pvr.zattoo/resources/settings.xml
 msgctxt "#30098"
@@ -233,3 +221,44 @@ msgctxt "#30203"
 msgid "Server not reachable!"
 msgstr ""
 
+#. Selection value for setting defined with id #30398
+#: pvr.zattoo/resources/settings.xml
+msgctxt "#30300"
+msgid "Auto"
+msgstr ""
+
+#. Selection value for setting defined with id #30398
+#: pvr.zattoo/resources/settings.xml
+msgctxt "#30301"
+msgid "1"
+msgstr ""
+
+#. Selection value for setting defined with id #30398
+#: pvr.zattoo/resources/settings.xml
+msgctxt "#30302"
+msgid "2"
+msgstr ""
+
+#. Selection value for setting defined with id #30398
+#: pvr.zattoo/resources/settings.xml
+msgctxt "#30303"
+msgid "3"
+msgstr ""
+
+#. Selection value for setting defined with id #30398
+#: pvr.zattoo/resources/settings.xml
+msgctxt "#30304"
+msgid "Disable DRM"
+msgstr ""
+
+#. Selection list on settings to get wanted stream provider
+#: pvr.zattoo/resources/settings.xml
+msgctxt "#30398"
+msgid "DRM Level"
+msgstr ""
+
+#. Help text to setting #30098
+#: pvr.zattoo/resources/settings.xml
+msgctxt "#30399"
+msgid "Set the supported DRM level. Auto uses L3 for Linux and L2 otherwise."
+msgstr ""
diff --git a/pvr.zattoo/resources/settings.xml b/pvr.zattoo/resources/settings.xml
index 2e242ef..6929fa9 100644
--- a/pvr.zattoo/resources/settings.xml
+++ b/pvr.zattoo/resources/settings.xml
@@ -27,17 +27,19 @@
           <default>false</default>
           <control type="toggle" />
         </setting>
-        <setting id="streamtype" type="integer" label="30004" help="30005">
+		<setting id="drmLevel" type="integer" label="30398" help="30399">
           <level>0</level>
           <default>0</default>
           <constraints>
             <options>
-              <option label="30006">0</option> <!-- dash -->
-              <option label="30007">1</option> <!-- hls -->
-              <option label="30008">2</option> <!-- dash_widevine -->
+              <option label="30300">0</option> <!-- Auto -->
+              <option label="30301">1</option>
+              <option label="30302">2</option>
+              <option label="30303">3</option>
+              <option label="30304">-1</option> <!-- DRM disabled -->
             </options>
           </constraints>
-          <control type="spinner" format="integer" />
+          <control type="list" format="integer" />
 		</setting>
 		<setting id="enableDolby" type="boolean" label="30010" help="30011">
           <level>0</level>
diff --git a/src/Session.cpp b/src/Session.cpp
index c62ac9c..302ab7d 100644
--- a/src/Session.cpp
+++ b/src/Session.cpp
@@ -70,6 +70,8 @@ void Session::LoginThread() {
     else
     {
       kodi::Log(ADDON_LOG_ERROR, "Login failed");
+      m_nextLoginAttempt = std::time(0) + 3600;
+      kodi::QueueNotification(QUEUE_ERROR, "", kodi::addon::GetLocalizedString(30201));
     }
   }
 }
diff --git a/src/Settings.cpp b/src/Settings.cpp
index 9a39b2c..1ef4934 100644
--- a/src/Settings.cpp
+++ b/src/Settings.cpp
@@ -39,7 +39,7 @@ bool CSettings::Load()
               "Couldn't get 'enableDolby' setting, falling back to 'true' as default");
     m_zatEnableDolby = true;
   }
-
+  
   if (!kodi::addon::CheckSettingBoolean("skipStart", m_skipStartOfProgramme))
   {
     /* If setting is unknown fallback to defaults */
@@ -48,13 +48,6 @@ bool CSettings::Load()
     m_skipStartOfProgramme = true;
   }
 
-  if (!kodi::addon::CheckSettingEnum<STREAM_TYPE>("streamtype", m_streamType))
-  {
-    /* If setting is unknown fallback to defaults */
-    kodi::Log(ADDON_LOG_ERROR, "Couldn't get 'streamtype' setting, falling back to 'DASH' as default");
-    m_streamType = DASH;
-  }
-
   if (!kodi::addon::CheckSettingString("parentalPin", m_parentalPin))
   {
     /* If setting is unknown fallback to defaults */
@@ -69,6 +62,14 @@ bool CSettings::Load()
               "Couldn't get 'provider' setting, falling back to '0' as default");
     m_provider = 0;
   }
+  
+  if (!kodi::addon::CheckSettingInt("drmLevel", m_drmLevel))
+  {
+    /* If setting is unknown fallback to defaults */
+    kodi::Log(ADDON_LOG_ERROR,
+              "Couldn't get 'drmLevel' setting, falling back to 'auto' as default");
+    m_drmLevel = 0;
+  }
 
   return true;
 }
@@ -118,16 +119,7 @@ ADDON_STATUS CSettings::SetSetting(const std::string& settingName,
     if (m_skipStartOfProgramme != settingValue.GetBoolean())
     {
       m_skipStartOfProgramme = settingValue.GetBoolean();
-      return ADDON_STATUS_NEED_RESTART;
-    }
-  }
-  else if (settingName == "streamtype")
-  {
-    kodi::Log(ADDON_LOG_DEBUG, "Changed Setting 'streamtype' from %u to %u", m_streamType, settingValue.GetInt());
-    if (m_streamType != settingValue.GetEnum<STREAM_TYPE>())
-    {
-      m_streamType = settingValue.GetEnum<STREAM_TYPE>();
-      return ADDON_STATUS_NEED_RESTART;
+      return ADDON_STATUS_OK;
     }
   }
   else if (settingName == "parentalPin")
@@ -137,7 +129,7 @@ ADDON_STATUS CSettings::SetSetting(const std::string& settingName,
     tmp_sParentalPin = m_parentalPin;
     m_parentalPin = settingValue.GetString();
     if (tmp_sParentalPin != m_parentalPin)
-      return ADDON_STATUS_NEED_RESTART;
+      return ADDON_STATUS_OK;
   }
   else if (settingName == "provider")
   {
@@ -148,6 +140,15 @@ ADDON_STATUS CSettings::SetSetting(const std::string& settingName,
       return ADDON_STATUS_NEED_RESTART;
     }
   }
+  else if (settingName == "drmLevel")
+  {
+    kodi::Log(ADDON_LOG_DEBUG, "Changed Setting 'drmLevel' from %u to %u", m_drmLevel, settingValue.GetInt());
+    if (m_drmLevel != settingValue.GetInt())
+    {
+      m_drmLevel = settingValue.GetInt();
+      return ADDON_STATUS_OK;
+    }
+  }
 
   return ADDON_STATUS_OK;
 }
diff --git a/src/Settings.h b/src/Settings.h
index d50696e..44e3727 100644
--- a/src/Settings.h
+++ b/src/Settings.h
@@ -9,13 +9,6 @@
 
 #include <kodi/AddonBase.h>
 
-enum STREAM_TYPE : int
-{
-  DASH,
-  HLS,
-  DASH_WIDEVINE
-};
-
 class ATTR_DLL_LOCAL CSettings
 {
 public:
@@ -30,7 +23,7 @@ public:
   bool GetZatFavoritesOnly() const { return m_zatFavoritesOnly; }
   bool GetZatEnableDolby() const { return m_zatEnableDolby; }
   bool GetSkipStartOfProgramme() const { return m_skipStartOfProgramme; }
-  STREAM_TYPE GetStreamType() const { return m_streamType; }
+  int DrmLevel() const { return m_drmLevel; }
   const std::string GetParentalPin() const { return m_parentalPin; }
   int GetProvider() const { return m_provider; }
 
@@ -40,7 +33,7 @@ private:
   bool m_zatFavoritesOnly = false;
   bool m_zatEnableDolby = true;
   bool m_skipStartOfProgramme = true;
-  STREAM_TYPE m_streamType = DASH;
+  int m_drmLevel = 0;
   std::string m_parentalPin;
   int m_provider = 0;
 };
diff --git a/src/Utils.cpp b/src/Utils.cpp
index 4d42a67..0319898 100644
--- a/src/Utils.cpp
+++ b/src/Utils.cpp
@@ -176,3 +176,12 @@ bool Utils::JsonBoolOrFalse(const rapidjson::Value& jsonValue, const char* field
 
   return false;
 }
+
+bool Utils::RunsOnLinux()
+{
+    #ifdef __linux__
+    return true;
+    #else
+    return false;
+    #endif
+}
diff --git a/src/Utils.h b/src/Utils.h
index 9af9eb2..8230690 100644
--- a/src/Utils.h
+++ b/src/Utils.h
@@ -22,4 +22,5 @@ public:
   static std::string JsonStringOrEmpty(const rapidjson::Value& jsonValue, const char* fieldName);
   static int JsonIntOrZero(const rapidjson::Value& jsonValue, const char* fieldName);
   static bool JsonBoolOrFalse(const rapidjson::Value& jsonValue, const char* fieldName);
+  static bool RunsOnLinux();
 };
diff --git a/src/ZatChannel.h b/src/ZatChannel.h
index 4c6dc38..01716fc 100644
--- a/src/ZatChannel.h
+++ b/src/ZatChannel.h
@@ -2,12 +2,14 @@
 #define SRC_ZATCHANNEL_H_
 
 #include <string>
+#include <vector>
 
 struct ZatChannel
 {
   int iUniqueId;
   int iChannelNumber;
   bool recordingEnabled;
+  std::vector<std::pair<std::string, bool>> qualityWithDrm;
   std::string name;
   std::string strLogoPath;
   std::string cid;
diff --git a/src/ZatData.cpp b/src/ZatData.cpp
index 69378e2..a2fca78 100644
--- a/src/ZatData.cpp
+++ b/src/ZatData.cpp
@@ -6,7 +6,6 @@
 #include <ctime>
 #include <utility>
 #include "Utils.h"
-#include "rapidjson/document.h"
 #include "rapidjson/writer.h"
 #include "rapidjson/stringbuffer.h"
 #include <kodi/Filesystem.h>
@@ -21,41 +20,15 @@ using namespace rapidjson;
 constexpr char app_token_file[] = "special://temp/zattoo_app_token";
 const char data_file[] = "special://profile/addon_data/pvr.zattoo/data.json";
 
-std::string ZatData::GetManifestType()
-{
-  switch (m_settings->GetStreamType())
-  {
-    case HLS:
-      return "hls";
-    default:
-      return "mpd";
-  }
-}
-
-std::string ZatData::GetMimeType()
-{
-  switch (m_settings->GetStreamType())
-  {
-    case HLS:
-      return "application/x-mpegURL";
-    default:
-      return "application/xml+dash";
-  }
-}
-
 void ZatData::SetStreamProperties(
     std::vector<kodi::addon::PVRStreamProperty>& properties,
     const std::string& url)
 {
   properties.emplace_back(PVR_STREAM_PROPERTY_STREAMURL, url);
   properties.emplace_back(PVR_STREAM_PROPERTY_INPUTSTREAM, "inputstream.adaptive");
-  properties.emplace_back("inputstream.adaptive.manifest_type", GetManifestType());
-  properties.emplace_back(PVR_STREAM_PROPERTY_MIMETYPE, GetMimeType());
-
-  if (m_settings->GetStreamType() == DASH || m_settings->GetStreamType() == DASH_WIDEVINE)
-  {
-    properties.emplace_back("inputstream.adaptive.manifest_update_parameter", "full");
-  }
+  properties.emplace_back("inputstream.adaptive.manifest_type", "mpd");
+  properties.emplace_back(PVR_STREAM_PROPERTY_MIMETYPE, "application/xml+dash");
+  properties.emplace_back("inputstream.adaptive.manifest_update_parameter", "full");
 }
 
 bool ZatData::ReadDataJson()
@@ -139,35 +112,39 @@ bool ZatData::LoadChannels()
   for (Value::ConstValueIterator itr1 = channels.Begin();
       itr1 != channels.End(); ++itr1)
   {
+    
     const Value& channelItem = (*itr1);
+    
+    ZatChannel channel;
+    std::string cid = Utils::JsonStringOrEmpty(channelItem, "cid");
+    channel.iUniqueId = Utils::GetChannelId(cid.c_str());
+    channel.cid = cid;
+    channel.iChannelNumber = ++channelNumber;
+    channel.recordingEnabled =
+        channelItem.HasMember("recording") ?
+            channelItem["recording"].GetBool() : false;
+    
     const Value& qualities = channelItem["qualities"];
     for (Value::ConstValueIterator itr2 = qualities.Begin();
         itr2 != qualities.End(); ++itr2)
     {
       const Value& qualityItem = (*itr2);
       std::string avail = Utils::JsonStringOrEmpty(qualityItem, "availability");
-      if (avail == "available")
-      {
-        ZatChannel channel;
+      if (avail != "available") {
+        continue;
+      }
+      bool drmRequired = qualityItem.HasMember("drm_required") ? qualityItem["drm_required"].GetBool() : false;
+      channel.qualityWithDrm.push_back({Utils::JsonStringOrEmpty(qualityItem, "level"), drmRequired});
+      if (channel.name.empty()) {
         channel.name = Utils::JsonStringOrEmpty(qualityItem, "title");
-        std::string cid = Utils::JsonStringOrEmpty(channelItem, "cid");
-        channel.iUniqueId = Utils::GetChannelId(cid.c_str());
-        channel.cid = cid;
-        channel.iChannelNumber = ++channelNumber;
-        channel.strLogoPath = "http://logos.zattic.com";
-        channel.strLogoPath.append(
-            Utils::JsonStringOrEmpty(qualityItem, "logo_white_84"));
-        channel.recordingEnabled =
-            channelItem.HasMember("recording") ?
-                channelItem["recording"].GetBool() : false;
-        PVRZattooChannelGroup &group = m_channelGroups[channelItem["group_index"].GetInt()];
-        group.channels.insert(group.channels.end(), channel);
-        allChannels[cid] = channel;
-        m_channelsByCid[channel.cid] = channel;
-        m_channelsByUid[channel.iUniqueId] = channel;
-        break;
+        channel.strLogoPath = "http://logos.zattic.com" + Utils::JsonStringOrEmpty(qualityItem, "logo_white_84");
       }
     }
+    PVRZattooChannelGroup &group = m_channelGroups[channelItem["group_index"].GetInt()];
+    group.channels.insert(group.channels.end(), channel);
+    allChannels[cid] = channel;
+    m_channelsByCid[channel.cid] = channel;
+    m_channelsByUid[channel.iUniqueId] = channel;
   }
 
   PVRZattooChannelGroup favGroup;
@@ -428,10 +405,12 @@ PVR_ERROR ZatData::GetChannels(bool radio, kodi::addon::PVRChannelsResultSet& re
   return PVR_ERROR_NO_ERROR;
 }
 
-std::string ZatData::GetStreamUrl(std::string& jsonString, std::vector<kodi::addon::PVRStreamProperty>& properties) {
-  Document doc;
-  doc.Parse(jsonString.c_str());
-  if (doc.GetParseError() || !doc.HasMember("stream"))
+bool ZatData::IsDrmLimitApplied(Document& doc) {
+  return doc.HasMember("drm_limit_applied") && doc["drm_limit_applied"].GetBool();
+}
+
+std::string ZatData::GetStreamUrl(Document& doc, std::vector<kodi::addon::PVRStreamProperty>& properties) {
+  if (!doc.HasMember("stream"))
   {
     return "";
   }
@@ -442,11 +421,9 @@ std::string ZatData::GetStreamUrl(std::string& jsonString, std::vector<kodi::add
     const Value& watchUrl = (*itr);
     kodi::Log(ADDON_LOG_DEBUG, "Selected url for maxrate: %d", watchUrl["maxrate"].GetInt());
     url = Utils::JsonStringOrEmpty(watchUrl, "url");
-    if (m_settings->GetStreamType() == DASH_WIDEVINE) {
-      std::string licenseUrl = Utils::JsonStringOrEmpty(watchUrl, "license_url");
-      properties.emplace_back("inputstream.adaptive.license_key", licenseUrl + "||A{SSM}|");
-      properties.emplace_back("inputstream.adaptive.license_type", "com.widevine.alpha");
-    }
+    std::string licenseUrl = Utils::JsonStringOrEmpty(watchUrl, "license_url");
+    properties.emplace_back("inputstream.adaptive.license_key", licenseUrl + "||A{SSM}|");
+    properties.emplace_back("inputstream.adaptive.license_type", "com.widevine.alpha");
     break;
   }
   kodi::Log(ADDON_LOG_DEBUG, "Got url: %s", url.c_str());
@@ -460,14 +437,35 @@ PVR_ERROR ZatData::GetChannelStreamProperties(const kodi::addon::PVRChannel& cha
 
   ZatChannel* ownChannel = FindChannel(channel.GetUniqueId());
   kodi::Log(ADDON_LOG_DEBUG, "Get live url for channel %s", ownChannel->cid.c_str());
+  
+  bool forceWithoutDrm = GetDrmLevel() <= 0;
+  Document doc;
+  
+  while (true) {
+    std::ostringstream dataStream;
+    bool requiresDrm;
+    dataStream << GetQualityStreamParameter(ownChannel->cid, forceWithoutDrm, requiresDrm);
+    dataStream << GetBasicStreamParameters(requiresDrm) << "&format=json&timeshift=10800";
+    kodi::Log(ADDON_LOG_INFO, "Stream properties: %s.", dataStream.str().c_str());
+    int statusCode;
+    std::string jsonString = m_httpClient->HttpPost(m_session->GetProviderUrl() + "/zapi/watch/live/" + ownChannel->cid, dataStream.str(), statusCode);
+   
+    doc.Parse(jsonString.c_str());
+    if (doc.GetParseError())
+    {
+      return ret;
+    }
 
-  std::ostringstream dataStream;
-  dataStream << GetStreamParameters() << "&format=json&timeshift=10800";
-
-  int statusCode;
-  std::string jsonString = m_httpClient->HttpPost(m_session->GetProviderUrl() + "/zapi/watch/live/" + ownChannel->cid, dataStream.str(), statusCode);
+    if (forceWithoutDrm || !IsDrmLimitApplied(doc)) {
+      break;
+    }
+    forceWithoutDrm = true;
+    kodi::Log(ADDON_LOG_INFO, "Fallback to no-drm version.");
+    doc.SetNull();
+    doc.GetAllocator().Clear();
+  }
 
-  std::string strUrl = GetStreamUrl(jsonString, properties);
+  std::string strUrl = GetStreamUrl(doc, properties);
   if (!strUrl.empty())
   {
     SetStreamProperties(properties, strUrl);
@@ -636,16 +634,6 @@ PVR_ERROR ZatData::GetTimers(kodi::addon::PVRTimersResultSet& results)
     const Value& recording = (*itr);
     int programId = recording["program_id"].GetInt();
 
-    std::string cid = Utils::JsonStringOrEmpty(recording, "cid");
-    auto iterator = m_channelsByCid.find(cid);
-    if (iterator == m_channelsByCid.end())
-    {
-      kodi::Log(ADDON_LOG_ERROR, "Channel %s not found for recording: %i",
-          cid.c_str(), programId);
-      continue;
-    }
-    ZatChannel channel = iterator->second;
-
     auto detailIterator = detailsById.find(programId);
     bool hasDetails = detailIterator != detailsById.end();
 
@@ -672,7 +660,14 @@ PVR_ERROR ZatData::GetTimers(kodi::addon::PVRTimersResultSet& results)
       tag.SetState(PVR_TIMER_STATE_SCHEDULED);
       tag.SetTimerType(1);
       tag.SetEPGUid(static_cast<unsigned int>(recording["program_id"].GetInt()));
-      tag.SetClientChannelUid(channel.iUniqueId);
+      std::string cid = Utils::JsonStringOrEmpty(recording, "cid");
+      auto iterator = m_channelsByCid.find(cid);
+      if (iterator != m_channelsByCid.end())
+      {
+        ZatChannel channel = iterator->second;
+        tag.SetClientChannelUid(channel.iUniqueId);
+      }
+      
       if (genre)
       {
         tag.SetGenreSubType(genre & 0x0F);
@@ -691,25 +686,21 @@ PVR_ERROR ZatData::GetTimers(kodi::addon::PVRTimersResultSet& results)
     {
       const Value& recording = (*itr);
       int tvSeriesId = recording["tv_series_id"].GetInt();
-  
+      
+      kodi::addon::PVRTimer tag;
+
       std::string cid = Utils::JsonStringOrEmpty(recording, "cid");
       auto iterator = m_channelsByCid.find(cid);
-      if (iterator == m_channelsByCid.end())
+      if (iterator != m_channelsByCid.end())
       {
-        kodi::Log(ADDON_LOG_ERROR, "Channel %s not found for series recording: %i",
-            cid.c_str(), tvSeriesId);
-        continue;
+        ZatChannel channel = iterator->second;
+        tag.SetClientChannelUid(channel.iUniqueId);
       }
-      ZatChannel channel = iterator->second;
-    
-      //genre
-      kodi::addon::PVRTimer tag;
-
+      
       tag.SetClientIndex(static_cast<unsigned int>(tvSeriesId));
       tag.SetTitle(Utils::JsonStringOrEmpty(recording, "title"));
       tag.SetState(PVR_TIMER_STATE_SCHEDULED);
       tag.SetTimerType(2);
-      tag.SetClientChannelUid(channel.iUniqueId);
       results.Add(tag);
     }
   }
@@ -796,7 +787,7 @@ PVR_ERROR ZatData::DeleteTimer(const kodi::addon::PVRTimer& timer, bool forceDel
     {
       const Value& recording = (*itr);
       
-      int seriesId = recording["tv_series_id"].GetInt();
+      unsigned int seriesId = recording["tv_series_id"].GetInt();
       
       if (seriesId == timer.GetClientIndex()) {
         recordingId = recording["id"].GetInt();
@@ -864,16 +855,6 @@ PVR_ERROR ZatData::GetRecordings(bool deleted, kodi::addon::PVRRecordingsResultS
     const Value& recording = (*itr);
     int programId = recording["program_id"].GetInt();
 
-    std::string cid = Utils::JsonStringOrEmpty(recording, "cid");
-    auto iterator = m_channelsByCid.find(cid);
-    if (iterator == m_channelsByCid.end())
-    {
-      kodi::Log(ADDON_LOG_ERROR, "Channel %s not found for recording: %i",
-          cid.c_str(), programId);
-      continue;
-    }
-    ZatChannel channel = iterator->second;
-
     auto detailIterator = detailsById.find(programId);
     bool hasDetails = detailIterator != detailsById.end();
 
@@ -900,8 +881,18 @@ PVR_ERROR ZatData::GetRecordings(bool deleted, kodi::addon::PVRRecordingsResultS
       std::string imageToken = Utils::JsonStringOrEmpty(recording, "image_token");
       std::string imageUrl = Utils::GetImageUrl(imageToken);;
       tag.SetIconPath(imageUrl);
-      tag.SetChannelUid(channel.iUniqueId);
-      tag.SetChannelName(channel.name);
+
+      std::string cid = Utils::JsonStringOrEmpty(recording, "cid");
+      auto iterator = m_channelsByCid.find(cid);
+      if (iterator != m_channelsByCid.end())
+      {
+        ZatChannel channel = iterator->second;
+        tag.SetChannelUid(channel.iUniqueId);
+        tag.SetChannelName(channel.name);
+      } else {
+        tag.SetChannelName(cid);
+      }      
+      
       time_t endTime = Utils::StringToTime(
           Utils::JsonStringOrEmpty(recording, "end").c_str());
       tag.SetRecordingTime(startTime);
@@ -969,41 +960,93 @@ PVR_ERROR ZatData::GetRecordingsAmount(bool deleted, int& amount)
   return PVR_ERROR_NO_ERROR;
 }
 
-std::string ZatData::GetStreamParameters() {
+std::string ZatData::GetBasicStreamParameters(bool requiresDrm) {
   std::string params = m_settings->GetZatEnableDolby() ? "&enable_eac3=true" : "";
-  params += "&stream_type=" + GetStreamTypeString();
+  
+  params += "&stream_type=" + GetStreamTypeString(requiresDrm);
+  int drmLevel = GetDrmLevel();
+  if (drmLevel > 0) {
+    params += "&max_drm_lvl=" + std::to_string(drmLevel);
+  }
 
   if (!m_settings->GetParentalPin().empty()) {
     params += "&youth_protection_pin=" + m_settings->GetParentalPin();
   }
-
+   
   return params;
 }
 
-std::string ZatData::GetStreamTypeString() {
-  switch (m_settings->GetStreamType()) {
-    case HLS:
-      return "hls7";
-    case DASH_WIDEVINE:
-      return "dash_widevine";
-    default:
-      return "dash";
+std::string ZatData::GetQualityStreamParameter(const std::string& cid, bool forceWithoutDrm, bool& requiresDrm) {
+  requiresDrm = !forceWithoutDrm;
+  auto iterator = m_channelsByCid.find(cid);
+  if (iterator != m_channelsByCid.end())
+  {
+    ZatChannel channel = iterator->second;
+    std::string selectedQuality;
+    for (auto const& pair : channel.qualityWithDrm)
+    {
+      if (!forceWithoutDrm || !pair.second) {
+        selectedQuality = pair.first;
+        requiresDrm = pair.second;
+        break;
+      }
+    }
+
+    if (!selectedQuality.empty()) {
+      kodi::Log(ADDON_LOG_INFO, "Selected quality: %s, requiring drm: %s", selectedQuality.c_str(), requiresDrm ? "true" : "false");
+      return "&quality=" + selectedQuality;
+    }
+  }
+  
+  return "";
+}
+
+int ZatData::GetDrmLevel() {
+  int drmLevel = m_settings->DrmLevel();
+  if (drmLevel == 0) {
+    return Utils::RunsOnLinux() ? 3 : 1;
+  } else {
+    return drmLevel;
   }
 }
 
+std::string ZatData::GetStreamTypeString(bool withDrm) {
+    if (!withDrm) {
+      return "dash";
+    }
+    return "dash_widevine";
+}
+
 PVR_ERROR ZatData::GetRecordingStreamProperties(const kodi::addon::PVRRecording& recording,
                                                 std::vector<kodi::addon::PVRStreamProperty>& properties)
 {
   kodi::Log(ADDON_LOG_DEBUG, "Get url for recording %s", recording.GetRecordingId().c_str());
-
+  PVR_ERROR ret = PVR_ERROR_FAILED;
+  
+  std::string cid = "";
+  if (m_channelsByUid.count(recording.GetChannelUid())) {
+    ZatChannel& channel = m_channelsByUid[recording.GetChannelUid()];
+    cid = channel.cid;
+  }
+  
+  Document doc;
+  
+  bool useWidevine = GetDrmLevel() > -1;
+  
   std::ostringstream dataStream;
-  dataStream << GetStreamParameters();
-
+  dataStream << GetBasicStreamParameters(useWidevine);
+  kodi::Log(ADDON_LOG_INFO, "Stream properties: %s.", dataStream.str().c_str());
   int statusCode;
+  
   std::string jsonString = m_httpClient->HttpPost(m_session->GetProviderUrl() + "/zapi/watch/recording/" + recording.GetRecordingId(), dataStream.str(), statusCode);
-
-  std::string strUrl = GetStreamUrl(jsonString, properties);
-  PVR_ERROR ret = PVR_ERROR_FAILED;
+  
+  doc.Parse(jsonString.c_str());
+  if (doc.GetParseError())
+  {
+    return ret;
+  }
+                  
+  std::string strUrl = GetStreamUrl(doc, properties);
   if (!strUrl.empty())
   {
     SetStreamProperties(properties, strUrl);
@@ -1124,18 +1167,36 @@ PVR_ERROR ZatData::GetEPGTagStreamProperties(const kodi::addon::PVREPGTag& tag,
 
 std::string ZatData::GetStreamUrlForProgram(const std::string& cid, int programId, std::vector<kodi::addon::PVRStreamProperty>& properties)
 {
-  std::ostringstream dataStream;
-
-  std::string jsonString;
-
   kodi::Log(ADDON_LOG_DEBUG, "Get timeshift url for channel %s and program %i", cid.c_str(), programId);
+  
+  bool forceWithoutDrm = GetDrmLevel() <= 0;
+  Document doc;
+  
+  while (true) {
+    std::ostringstream dataStream;
+    bool requiresDrm;
+    dataStream << GetQualityStreamParameter(cid, forceWithoutDrm, requiresDrm);
+    dataStream << GetBasicStreamParameters(requiresDrm);
+    dataStream << "&pre_padding=0&post_padding=0";
+    kodi::Log(ADDON_LOG_INFO, "Stream properties: %s.", dataStream.str().c_str());
+    int statusCode;
+    std::string jsonString = m_httpClient->HttpPost(m_session->GetProviderUrl() + "/zapi/v3/watch/replay/" + cid + "/" + std::to_string(programId), dataStream.str(), statusCode);
+    doc.Parse(jsonString.c_str());
+    if (doc.GetParseError())
+    {
+      return "";
+    }
 
-  dataStream << GetStreamParameters();
-  dataStream << "&pre_padding=0&post_padding=0";
-  int statusCode;
-  jsonString = m_httpClient->HttpPost(m_session->GetProviderUrl() + "/zapi/v3/watch/replay/" + cid + "/" + std::to_string(programId), dataStream.str(), statusCode);
-
-  std::string strUrl = GetStreamUrl(jsonString, properties);
+    if (forceWithoutDrm || !IsDrmLimitApplied(doc)) {
+      break;
+    }
+    forceWithoutDrm = true;
+    kodi::Log(ADDON_LOG_INFO, "Fallback to no-drm version.");
+    doc.SetNull();
+    doc.GetAllocator().Clear();
+  }
+  
+  std::string strUrl = GetStreamUrl(doc, properties);
   return strUrl;
 }
 
@@ -1172,7 +1233,7 @@ bool ZatData::SessionInitialized()
   if (m_epgProvider) {
     delete m_epgProvider;
   }
-  kodi::Log(ADDON_LOG_INFO, "Stream type: %s", GetStreamTypeString().c_str());
+  kodi::Log(ADDON_LOG_INFO, "DRM Level: %i", m_settings->DrmLevel());
   if (!LoadChannels()) {
     return false;
   }
diff --git a/src/ZatData.h b/src/ZatData.h
index 8ba9e43..49c59cb 100644
--- a/src/ZatData.h
+++ b/src/ZatData.h
@@ -104,14 +104,15 @@ private:
   bool ReinitSession();
   ZatChannel* FindChannel(int uniqueId);
   PVRZattooChannelGroup* FindGroup(const std::string& strName);
-  std::string GetStreamTypeString();
-  std::string GetStreamUrl(std::string& jsonString, std::vector<kodi::addon::PVRStreamProperty>& properties);
-  std::string GetStreamParameters();
+  std::string GetStreamTypeString(bool withDrm);
+  bool IsDrmLimitApplied(rapidjson::Document& doc);
+  std::string GetStreamUrl(rapidjson::Document& doc, std::vector<kodi::addon::PVRStreamProperty>& properties);
+  std::string GetBasicStreamParameters(bool requiresDrm);
+  std::string GetQualityStreamParameter(const std::string& cid, bool withoutDrm, bool& requiresDrm);
+  int GetDrmLevel();
   bool ParseRecordingsTimers(const rapidjson::Value& recordings, std::map<int, ZatRecordingDetails>& detailsById);
   void AddTimerType(std::vector<kodi::addon::PVRTimerType>& types, int idx, int attributes);
   bool Record(int programId, bool series);
-  std::string GetManifestType();
-  std::string GetMimeType();
   void SetStreamProperties(std::vector<kodi::addon::PVRStreamProperty>& properties, const std::string& url);
   std::string GetStreamUrlForProgram(const std::string& cid, int programId, std::vector<kodi::addon::PVRStreamProperty>& properties);
   bool TryToReinitIf403(int statusCode);
diff --git a/src/http/HttpClient.cpp b/src/http/HttpClient.cpp
index e94f71e..406ee8b 100644
--- a/src/http/HttpClient.cpp
+++ b/src/http/HttpClient.cpp
@@ -14,7 +14,7 @@ HttpClient::HttpClient(ParameterDB *parameterDB):
   kodi::Log(ADDON_LOG_INFO, "Using useragent: %s", USER_AGENT.c_str());
 
   m_uuid = m_parameterDB->Get("uuid");
-  m_zattooSession = m_parameterDB->Get("zattooSession");  
+  m_beakerSessionId = m_parameterDB->Get("beakerSessionId");  
 }
 
 HttpClient::~HttpClient()
@@ -110,11 +110,6 @@ std::string HttpClient::HttpRequest(const std::string& action, const std::string
   {
     cookie += "uuid=" + m_uuid + "; ";
   }
-
-  if (!m_zattooSession.empty())
-  {
-    cookie += "zattoo.session=" + m_zattooSession + "; ";
-  }
   
   if (!cookie.empty()) {
     curl.AddOption("Cookie", cookie);
@@ -137,14 +132,7 @@ std::string HttpClient::HttpRequest(const std::string& action, const std::string
     kodi::Log(ADDON_LOG_DEBUG, "Got new beaker.session.id: %s..",
         sessionId.substr(0, 5).c_str());
     m_beakerSessionId = sessionId;
-  }
-
-  std::string zattooSession = curl.GetCookie("zattoo.session");
-  if (!zattooSession.empty() && m_zattooSession != zattooSession)
-  {
-    kodi::Log(ADDON_LOG_DEBUG, "Got new zattooSession: %s..", zattooSession.substr(0, 5).c_str());
-    m_zattooSession = zattooSession;
-    m_parameterDB->Set("zattooSession", zattooSession);
+    m_parameterDB->Set("beakerSessionId", sessionId);
   }
 
   return content;
diff --git a/src/http/HttpClient.h b/src/http/HttpClient.h
index 1b055f2..10028a1 100644
--- a/src/http/HttpClient.h
+++ b/src/http/HttpClient.h
@@ -24,7 +24,6 @@ private:
   std::string HttpRequestToCurl(Curl &curl, const std::string& action, const std::string& url, const std::string& postData, int &statusCode);
   std::string GenerateUUID();
   std::string m_beakerSessionId;
-  std::string m_zattooSession;
   std::string m_uuid;
   ParameterDB *m_parameterDB;
   HttpStatusCodeHandler *m_statusCodeHandler = nullptr;

Debdiff

[The following lists of changes regard files as different if they have different names, permissions or owners.]

Files in second set of .debs but not in first

-rw-r--r--  root/root   /usr/lib/debug/.build-id/dc/b328dbb28e7037cbd03c17fca28498ab3920b0.debug
-rw-r--r--  root/root   /usr/lib/x86_64-linux-gnu/kodi/addons/pvr.zattoo/pvr.zattoo.so.20.3.13
lrwxrwxrwx  root/root   /usr/lib/x86_64-linux-gnu/kodi/addons/pvr.zattoo/pvr.zattoo.so.20.2 -> pvr.zattoo.so.20.3.13

Files in first set of .debs but not in second

-rw-r--r--  root/root   /usr/lib/debug/.build-id/2b/6a8a6471383144430456a89f5eb71e18e6dfa5.debug
-rw-r--r--  root/root   /usr/lib/x86_64-linux-gnu/kodi/addons/pvr.zattoo/pvr.zattoo.so.20.3.3
lrwxrwxrwx  root/root   /usr/lib/x86_64-linux-gnu/kodi/addons/pvr.zattoo/pvr.zattoo.so.20.2 -> pvr.zattoo.so.20.3.3

No differences were encountered between the control files of package kodi-pvr-zattoo

Control files of package kodi-pvr-zattoo-dbgsym: lines which differ (wdiff format)

  • Build-Ids: 2b6a8a6471383144430456a89f5eb71e18e6dfa5 dcb328dbb28e7037cbd03c17fca28498ab3920b0

More details

Full run details