Repository: postlund/pyatv
Pull request #2478 (2024-08-12 18:01:47)

feat: Add iTunes Store Identifier to metadata
> This is a re pull request of #2439 . When I rebased onto Ralph the PR closed. And I couldn't reopen 🤷‍♂️

> Sorry I didn't get this in before Ralph. It crazy how life can sometimes make running `./scripts/chickn.py -t fixup`  more difficult then it needs to be. No rush on when this gets in. Thank you. 

> Hopefully I rebased without issue. 

Original PR description
---

- Added `iTunesStoreIdentifier` to `FeatureName` in `pyatv/const.py`.
- Updated `Playing` class in `pyatv/interface.py` to include `itunes_store_identifier`.
- Enhanced the `metadata_field` function in `pyatv/protocols/mrp/__init__.py` to support `itunes_store_identifier`.
- Adjusted fake device implementation in `tests/fake_device/mrp.py` to handle `itunes_store_identifier`.
- Updated functional tests in `tests/protocols/mrp/test_mrp_functional.py` to verify `itunes_store_identifier`.
- Modified documentation to include `itunes_store_identifier`.



Honestly first time really working in a mature python codebase so hopefully this checks all the boxes. I tried to follow the guidelines. But am leaning on this portion of the MD file lol

> PRs will not be merged unless all criterias are met. If you are unsure about anything, just push a PR and ask for help in the PR itself 😄

The tool Im building was really hinging on the ability to identify the content from the Apple TV App, but seemed like they only supplied the Title (which was just the series name if it was a tv show, not even the episode name. You would think apple would be better at implementing their own interfaces but I digress 😆) . But with some debugging I saw that iTunesStoreIdentifier was typically available for the Apple TV App so I exposed that the the Playing interface. 
Im currently using a local version with these changes for what im building but wanted to at least submit a PR if it proved useful for someone else.




JarekToro: I believe so.

https://developer.apple.com/documentation/storekit/skstoreproductparameteritunesitemidentifier

> The value for this key, an iTunes item identifier, is an instance of NSNumber.
To find a product’s iTunes identifier, go to linkmaker.itunes.apple.com and search for the product, then locate the iTunes identifier in the link URL. For example, the iTunes identifier for the iBooks app is 364709193.
postlund: > I believe so.
> 
> https://developer.apple.com/documentation/storekit/skstoreproductparameteritunesitemidentifier
> 
> > The value for this key, an iTunes item identifier, is an instance of NSNumber.
> > To find a product’s iTunes identifier, go to linkmaker.itunes.apple.com and search for the product, then locate the iTunes identifier in the link URL. For example, the iTunes identifier for the iBooks app is 364709193.

Can confirm just based on the protobuf definitions:

https://github.com/postlund/pyatv/blob/0f3540319a58fe9ca3440315a9d5a09572f5e351/pyatv/protocols/mrp/protobuf/ContentItemMetadata.proto#L169
postlund: Looks good to me, will merge once the checks passes 👍

Patch:
diff --git a/docs/api/pyatv.const.html b/docs/api/pyatv.const.html
index 8852e706..7ae5c65f 100644
--- a/docs/api/pyatv.const.html
+++ b/docs/api/pyatv.const.html
@@ -48,7 +48,7 @@ link_group: api
 </li>
 <li>
 <h4><code><a title="pyatv.const.FeatureName" href="#pyatv.const.FeatureName">FeatureName</a></code></h4>
-<ul class="two-column">
+<ul class="">
 <li><code><a title="pyatv.const.FeatureName.AccountList" href="#pyatv.const.FeatureName.AccountList">AccountList</a></code></li>
 <li><code><a title="pyatv.const.FeatureName.Action" href="#pyatv.const.FeatureName.Action">Action</a></code></li>
 <li><code><a title="pyatv.const.FeatureName.AddOutputDevices" href="#pyatv.const.FeatureName.AddOutputDevices">AddOutputDevices</a></code></li>
@@ -114,6 +114,7 @@ link_group: api
 <li><code><a title="pyatv.const.FeatureName.VolumeDown" href="#pyatv.const.FeatureName.VolumeDown">VolumeDown</a></code></li>
 <li><code><a title="pyatv.const.FeatureName.VolumeUp" href="#pyatv.const.FeatureName.VolumeUp">VolumeUp</a></code></li>
 <li><code><a title="pyatv.const.FeatureName.WakeUp" href="#pyatv.const.FeatureName.WakeUp">WakeUp</a></code></li>
+<li><code><a title="pyatv.const.FeatureName.iTunesStoreIdentifier" href="#pyatv.const.FeatureName.iTunesStoreIdentifier">iTunesStoreIdentifier</a></code></li>
 </ul>
 </li>
 <li>
@@ -223,7 +224,7 @@ link_group: api
 </header>
 <section id="section-intro">
 <p>Constants used in the public API.</p>
-<div class="git-link-div"><a href="https://github.com/postlund/pyatv/blob/master/pyatv/const.py#L1-L454" class="git-link">Browse git</a></div>
+<div class="git-link-div"><a href="https://github.com/postlund/pyatv/blob/master/pyatv/const.py#L1-L457" class="git-link">Browse git</a></div>
 </section>
 <section>
 </section>
@@ -348,7 +349,7 @@ future.</p></section>
 </code></dt>
 <dd>
 <section class="desc"><p>All supported features.</p></section>
-<div class="git-link-div"><a href="https://github.com/postlund/pyatv/blob/master/pyatv/const.py#L249-L445" class="git-link">Browse git</a></div>
+<div class="git-link-div"><a href="https://github.com/postlund/pyatv/blob/master/pyatv/const.py#L249-L448" class="git-link">Browse git</a></div>
 <h3>Ancestors</h3>
 <ul class="hlist">
 <li>enum.Enum</li>
@@ -615,6 +616,10 @@ future.</p></section>
 <dd>
 <section class="desc"><p>Wake up device (deprecated; use Power.turn_on).</p></section>
 </dd>
+<dt id="pyatv.const.FeatureName.iTunesStoreIdentifier"><code class="name">var <span class="ident">iTunesStoreIdentifier</span> = 50</code></dt>
+<dd>
+<section class="desc"><p>iTunes Store Identifier for Content</p></section>
+</dd>
 </dl>
 </dd>
 <dt id="pyatv.const.FeatureState"><code class="flex name class">
@@ -931,7 +936,7 @@ is Apple TV Software (pre-tvOS).</p></section>
 </code></dt>
 <dd>
 <section class="desc"><p>Touch action constants.</p></section>
-<div class="git-link-div"><a href="https://github.com/postlund/pyatv/blob/master/pyatv/const.py#L448-L454" class="git-link">Browse git</a></div>
+<div class="git-link-div"><a href="https://github.com/postlund/pyatv/blob/master/pyatv/const.py#L451-L457" class="git-link">Browse git</a></div>
 <h3>Ancestors</h3>
 <ul class="hlist">
 <li>enum.Enum</li>
diff --git a/docs/api/pyatv.interface.html b/docs/api/pyatv.interface.html
index a216b5ce..b32de95f 100644
--- a/docs/api/pyatv.interface.html
+++ b/docs/api/pyatv.interface.html
@@ -213,7 +213,7 @@ link_group: api
 </li>
 <li>
 <h4><code><a title="pyatv.interface.Playing" href="#pyatv.interface.Playing">Playing</a></code></h4>
-<ul class="two-column">
+<ul class="">
 <li><code><a title="pyatv.interface.Playing.album" href="#pyatv.interface.Playing.album">album</a></code></li>
 <li><code><a title="pyatv.interface.Playing.artist" href="#pyatv.interface.Playing.artist">artist</a></code></li>
 <li><code><a title="pyatv.interface.Playing.content_identifier" href="#pyatv.interface.Playing.content_identifier">content_identifier</a></code></li>
@@ -221,6 +221,7 @@ link_group: api
 <li><code><a title="pyatv.interface.Playing.episode_number" href="#pyatv.interface.Playing.episode_number">episode_number</a></code></li>
 <li><code><a title="pyatv.interface.Playing.genre" href="#pyatv.interface.Playing.genre">genre</a></code></li>
 <li><code><a title="pyatv.interface.Playing.hash" href="#pyatv.interface.Playing.hash">hash</a></code></li>
+<li><code><a title="pyatv.interface.Playing.itunes_store_identifier" href="#pyatv.interface.Playing.itunes_store_identifier">itunes_store_identifier</a></code></li>
 <li><code><a title="pyatv.interface.Playing.media_type" href="#pyatv.interface.Playing.media_type">media_type</a></code></li>
 <li><code><a title="pyatv.interface.Playing.position" href="#pyatv.interface.Playing.position">position</a></code></li>
 <li><code><a title="pyatv.interface.Playing.repeat" href="#pyatv.interface.Playing.repeat">repeat</a></code></li>
@@ -345,7 +346,7 @@ link_group: api
 <p>Public interface exposed by library.</p>
 <p>This module contains all the interfaces that represents a generic Apple TV device and
 all its features.</p>
-<div class="git-link-div"><a href="https://github.com/postlund/pyatv/blob/master/pyatv/interface.py#L1-L1573" class="git-link">Browse git</a></div>
+<div class="git-link-div"><a href="https://github.com/postlund/pyatv/blob/master/pyatv/interface.py#L1-L1586" class="git-link">Browse git</a></div>
 </section>
 <section>
 </section>
@@ -374,7 +375,7 @@ all its features.</p>
 <dd>
 <section class="desc"><p>Information about an app.</p>
 <p>Initialize a new App instance.</p></section>
-<div class="git-link-div"><a href="https://github.com/postlund/pyatv/blob/master/pyatv/interface.py#L680-L706" class="git-link">Browse git</a></div>
+<div class="git-link-div"><a href="https://github.com/postlund/pyatv/blob/master/pyatv/interface.py#L693-L719" class="git-link">Browse git</a></div>
 <h3>Instance variables</h3>
 <dl>
 <dt id="pyatv.interface.App.identifier"><code class="name">var <span class="ident">identifier</span> -> str</code></dt>
@@ -395,7 +396,7 @@ all its features.</p>
 <section class="desc"><p>Base class representing an Apple TV.</p>
 <p>Listener interface: <code>pyatv.interfaces.DeviceListener</code></p>
 <p>Initialize a new StateProducer instance.</p></section>
-<div class="git-link-div"><a href="https://github.com/postlund/pyatv/blob/master/pyatv/interface.py#L1488-L1573" class="git-link">Browse git</a></div>
+<div class="git-link-div"><a href="https://github.com/postlund/pyatv/blob/master/pyatv/interface.py#L1501-L1586" class="git-link">Browse git</a></div>
 <h3>Ancestors</h3>
 <ul class="hlist">
 <li>abc.ABC</li>
@@ -491,7 +492,7 @@ all its features.</p>
 </code></dt>
 <dd>
 <section class="desc"><p>Base class for app handling.</p></section>
-<div class="git-link-div"><a href="https://github.com/postlund/pyatv/blob/master/pyatv/interface.py#L709-L720" class="git-link">Browse git</a></div>
+<div class="git-link-div"><a href="https://github.com/postlund/pyatv/blob/master/pyatv/interface.py#L722-L733" class="git-link">Browse git</a></div>
 <h3>Subclasses</h3>
 <ul class="hlist">
 <li>pyatv.core.facade.FacadeApps</li>
@@ -565,7 +566,7 @@ all its features.</p>
 <p>Volume level is managed in percent where 0 is muted and 100 is max volume.</p>
 <p>Listener interface: <code>pyatv.interfaces.AudioListener</code></p>
 <p>Initialize a new StateProducer instance.</p></section>
-<div class="git-link-div"><a href="https://github.com/postlund/pyatv/blob/master/pyatv/interface.py#L1138-L1207" class="git-link">Browse git</a></div>
+<div class="git-link-div"><a href="https://github.com/postlund/pyatv/blob/master/pyatv/interface.py#L1151-L1220" class="git-link">Browse git</a></div>
 <h3>Ancestors</h3>
 <ul class="hlist">
 <li>abc.ABC</li>
@@ -682,7 +683,7 @@ possible and supported).</p></section>
 </code></dt>
 <dd>
 <section class="desc"><p>Listener interface for audio updates.</p></section>
-<div class="git-link-div"><a href="https://github.com/postlund/pyatv/blob/master/pyatv/interface.py#L1122-L1135" class="git-link">Browse git</a></div>
+<div class="git-link-div"><a href="https://github.com/postlund/pyatv/blob/master/pyatv/interface.py#L1135-L1148" class="git-link">Browse git</a></div>
 <h3>Ancestors</h3>
 <ul class="hlist">
 <li>abc.ABC</li>
@@ -721,7 +722,7 @@ possible and supported).</p></section>
 several services depending on the protocols it supports, e.g. DMAP or
 AirPlay.</p>
 <p>Initialize a new BaseConfig instance.</p></section>
-<div class="git-link-div"><a href="https://github.com/postlund/pyatv/blob/master/pyatv/interface.py#L1294-L1441" class="git-link">Browse git</a></div>
+<div class="git-link-div"><a href="https://github.com/postlund/pyatv/blob/master/pyatv/interface.py#L1307-L1454" class="git-link">Browse git</a></div>
 <h3>Ancestors</h3>
 <ul class="hlist">
 <li>abc.ABC</li>
@@ -903,7 +904,7 @@ original value).</p></section>
 <dd>
 <section class="desc"><p>General information about device.</p>
 <p>Initialize a new DeviceInfo instance.</p></section>
-<div class="git-link-div"><a href="https://github.com/postlund/pyatv/blob/master/pyatv/interface.py#L929-L1055" class="git-link">Browse git</a></div>
+<div class="git-link-div"><a href="https://github.com/postlund/pyatv/blob/master/pyatv/interface.py#L942-L1068" class="git-link">Browse git</a></div>
 <h3>Class variables</h3>
 <dl>
 <dt id="pyatv.interface.DeviceInfo.OUTPUT_DEVICE_ID"><code class="name">var <span class="ident">OUTPUT_DEVICE_ID</span></code></dt>
@@ -956,7 +957,7 @@ then this property contains the raw model string (if any is available).</p></sec
 </code></dt>
 <dd>
 <section class="desc"><p>Listener interface for generic device updates.</p></section>
-<div class="git-link-div"><a href="https://github.com/postlund/pyatv/blob/master/pyatv/interface.py#L881-L892" class="git-link">Browse git</a></div>
+<div class="git-link-div"><a href="https://github.com/postlund/pyatv/blob/master/pyatv/interface.py#L894-L905" class="git-link">Browse git</a></div>
 <h3>Ancestors</h3>
 <ul class="hlist">
 <li>abc.ABC</li>
@@ -1014,7 +1015,7 @@ then this property contains the raw model string (if any is available).</p></sec
 </code></dt>
 <dd>
 <section class="desc"><p>Base class for supported feature functionality.</p></section>
-<div class="git-link-div"><a href="https://github.com/postlund/pyatv/blob/master/pyatv/interface.py#L1058-L1090" class="git-link">Browse git</a></div>
+<div class="git-link-div"><a href="https://github.com/postlund/pyatv/blob/master/pyatv/interface.py#L1071-L1103" class="git-link">Browse git</a></div>
 <h3>Subclasses</h3>
 <ul class="hlist">
 <li>pyatv.core.facade.FacadeFeatures</li>
@@ -1064,7 +1065,7 @@ in one of the listed states.</p></section>
 <p>Listener
 interface: <code>pyatv.interfaces.KeyboardListener</code></p>
 <p>Initialize a new StateProducer instance.</p></section>
-<div class="git-link-div"><a href="https://github.com/postlund/pyatv/blob/master/pyatv/interface.py#L1221-L1252" class="git-link">Browse git</a></div>
+<div class="git-link-div"><a href="https://github.com/postlund/pyatv/blob/master/pyatv/interface.py#L1234-L1265" class="git-link">Browse git</a></div>
 <h3>Ancestors</h3>
 <ul class="hlist">
 <li>abc.ABC</li>
@@ -1140,7 +1141,7 @@ interface: <code>pyatv.interfaces.KeyboardListener</code></p>
 </code></dt>
 <dd>
 <section class="desc"><p>Listener interface for keyboard updates.</p></section>
-<div class="git-link-div"><a href="https://github.com/postlund/pyatv/blob/master/pyatv/interface.py#L1210-L1218" class="git-link">Browse git</a></div>
+<div class="git-link-div"><a href="https://github.com/postlund/pyatv/blob/master/pyatv/interface.py#L1223-L1231" class="git-link">Browse git</a></div>
 <h3>Ancestors</h3>
 <ul class="hlist">
 <li>abc.ABC</li>
@@ -1197,7 +1198,7 @@ interface: <code>pyatv.interfaces.KeyboardListener</code></p>
 </code></dt>
 <dd>
 <section class="desc"><p>Base class for retrieving metadata from an Apple TV.</p></section>
-<div class="git-link-div"><a href="https://github.com/postlund/pyatv/blob/master/pyatv/interface.py#L766-L806" class="git-link">Browse git</a></div>
+<div class="git-link-div"><a href="https://github.com/postlund/pyatv/blob/master/pyatv/interface.py#L779-L819" class="git-link">Browse git</a></div>
 <h3>Subclasses</h3>
 <ul class="hlist">
 <li>pyatv.core.facade.FacadeMetadata</li>
@@ -1259,7 +1260,7 @@ aspect ratio.</p></section>
 <dd>
 <section class="desc"><p>Information about an output device.</p>
 <p>Initialize a new OutputDevice instance.</p></section>
-<div class="git-link-div"><a href="https://github.com/postlund/pyatv/blob/master/pyatv/interface.py#L1093-L1119" class="git-link">Browse git</a></div>
+<div class="git-link-div"><a href="https://github.com/postlund/pyatv/blob/master/pyatv/interface.py#L1106-L1132" class="git-link">Browse git</a></div>
 <h3>Instance variables</h3>
 <dl>
 <dt id="pyatv.interface.OutputDevice.identifier"><code class="name">var <span class="ident">identifier</span> -> str</code></dt>
@@ -1345,12 +1346,12 @@ aspect ratio.</p></section>
 </dd>
 <dt id="pyatv.interface.Playing"><code class="flex name class">
 <span>class <span class="ident">Playing</span></span>
-<span>(</span><span>media_type: <a title="pyatv.const.MediaType" href="../const#pyatv.const.MediaType">MediaType</a> = MediaType.Unknown, device_state: <a title="pyatv.const.DeviceState" href="../const#pyatv.const.DeviceState">DeviceState</a> = DeviceState.Idle, title: Optional[str] = None, artist: Optional[str] = None, album: Optional[str] = None, genre: Optional[str] = None, total_time: Optional[int] = None, position: Optional[int] = None, shuffle: Optional[<a title="pyatv.const.ShuffleState" href="../const#pyatv.const.ShuffleState">ShuffleState</a>] = None, repeat: Optional[<a title="pyatv.const.RepeatState" href="../const#pyatv.const.RepeatState">RepeatState</a>] = None, hash: Optional[str] = None, series_name: Optional[str] = None, season_number: Optional[int] = None, episode_number: Optional[int] = None, content_identifier: Optional[str] = None)</span>
+<span>(</span><span>media_type: <a title="pyatv.const.MediaType" href="../const#pyatv.const.MediaType">MediaType</a> = MediaType.Unknown, device_state: <a title="pyatv.const.DeviceState" href="../const#pyatv.const.DeviceState">DeviceState</a> = DeviceState.Idle, title: Optional[str] = None, artist: Optional[str] = None, album: Optional[str] = None, genre: Optional[str] = None, total_time: Optional[int] = None, position: Optional[int] = None, shuffle: Optional[<a title="pyatv.const.ShuffleState" href="../const#pyatv.const.ShuffleState">ShuffleState</a>] = None, repeat: Optional[<a title="pyatv.const.RepeatState" href="../const#pyatv.const.RepeatState">RepeatState</a>] = None, hash: Optional[str] = None, series_name: Optional[str] = None, season_number: Optional[int] = None, episode_number: Optional[int] = None, content_identifier: Optional[str] = None, itunes_store_identifier: Optional[int] = None)</span>
 </code></dt>
 <dd>
 <section class="desc"><p>Base class for retrieving what is currently playing.</p>
 <p>Initialize a new Playing instance.</p></section>
-<div class="git-link-div"><a href="https://github.com/postlund/pyatv/blob/master/pyatv/interface.py#L459-L677" class="git-link">Browse git</a></div>
+<div class="git-link-div"><a href="https://github.com/postlund/pyatv/blob/master/pyatv/interface.py#L459-L690" class="git-link">Browse git</a></div>
 <h3>Ancestors</h3>
 <ul class="hlist">
 <li>abc.ABC</li>
@@ -1387,6 +1388,10 @@ aspect ratio.</p></section>
 <p>The hash is based on title, artist, album and total time. It should
 always be the same for the same content, but it is not guaranteed.</p></section>
 </dd>
+<dt id="pyatv.interface.Playing.itunes_store_identifier"><code class="name">var <span class="ident">itunes_store_identifier</span> -> Optional[int]</code></dt>
+<dd>
+<section class="desc"><p>Itunes Store identifier.</p></section>
+</dd>
 <dt id="pyatv.interface.Playing.media_type"><code class="name">var <span class="ident">media_type</span> -> <a title="pyatv.const.MediaType" href="../const#pyatv.const.MediaType">MediaType</a></code></dt>
 <dd>
 <section class="desc"><p>Type of media is currently playing, e.g. video, music.</p></section>
@@ -1429,7 +1434,7 @@ always be the same for the same content, but it is not guaranteed.</p></section>
 <section class="desc"><p>Base class for retrieving power state from an Apple TV.</p>
 <p>Listener interface: <code>pyatv.interfaces.PowerListener</code></p>
 <p>Initialize a new StateProducer instance.</p></section>
-<div class="git-link-div"><a href="https://github.com/postlund/pyatv/blob/master/pyatv/interface.py#L906-L926" class="git-link">Browse git</a></div>
+<div class="git-link-div"><a href="https://github.com/postlund/pyatv/blob/master/pyatv/interface.py#L919-L939" class="git-link">Browse git</a></div>
 <h3>Ancestors</h3>
 <ul class="hlist">
 <li>abc.ABC</li>
@@ -1482,7 +1487,7 @@ always be the same for the same content, but it is not guaranteed.</p></section>
 </code></dt>
 <dd>
 <section class="desc"><p>Listener interface for power updates.</p></section>
-<div class="git-link-div"><a href="https://github.com/postlund/pyatv/blob/master/pyatv/interface.py#L895-L903" class="git-link">Browse git</a></div>
+<div class="git-link-div"><a href="https://github.com/postlund/pyatv/blob/master/pyatv/interface.py#L908-L916" class="git-link">Browse git</a></div>
 <h3>Ancestors</h3>
 <ul class="hlist">
 <li>abc.ABC</li>
@@ -1510,7 +1515,7 @@ always be the same for the same content, but it is not guaranteed.</p></section>
 </code></dt>
 <dd>
 <section class="desc"><p>Listener interface for push updates.</p></section>
-<div class="git-link-div"><a href="https://github.com/postlund/pyatv/blob/master/pyatv/interface.py#L809-L818" class="git-link">Browse git</a></div>
+<div class="git-link-div"><a href="https://github.com/postlund/pyatv/blob/master/pyatv/interface.py#L822-L831" class="git-link">Browse git</a></div>
 <h3>Ancestors</h3>
 <ul class="hlist">
 <li>abc.ABC</li>
@@ -1551,7 +1556,7 @@ always be the same for the same content, but it is not guaranteed.</p></section>
 actually changes.</p>
 <p>Listener interface: <code><a title="pyatv.interface.PushListener" href="#pyatv.interface.PushListener">PushListener</a></code>.</p>
 <p>Initialize a new StateProducer instance.</p></section>
-<div class="git-link-div"><a href="https://github.com/postlund/pyatv/blob/master/pyatv/interface.py#L821-L848" class="git-link">Browse git</a></div>
+<div class="git-link-div"><a href="https://github.com/postlund/pyatv/blob/master/pyatv/interface.py#L834-L861" class="git-link">Browse git</a></div>
 <h3>Ancestors</h3>
 <ul class="hlist">
 <li>abc.ABC</li>
@@ -1951,7 +1956,7 @@ time interval is used, which is typically 10, 15, 30, etc. seconds.</p></section
 </code></dt>
 <dd>
 <section class="desc"><p>Base class for storage modules.</p></section>
-<div class="git-link-div"><a href="https://github.com/postlund/pyatv/blob/master/pyatv/interface.py#L1444-L1485" class="git-link">Browse git</a></div>
+<div class="git-link-div"><a href="https://github.com/postlund/pyatv/blob/master/pyatv/interface.py#L1457-L1498" class="git-link">Browse git</a></div>
 <h3>Ancestors</h3>
 <ul class="hlist">
 <li>abc.ABC</li>
@@ -2024,7 +2029,7 @@ the storage.</p></section>
 </code></dt>
 <dd>
 <section class="desc"><p>Base class for stream functionality.</p></section>
-<div class="git-link-div"><a href="https://github.com/postlund/pyatv/blob/master/pyatv/interface.py#L851-L878" class="git-link">Browse git</a></div>
+<div class="git-link-div"><a href="https://github.com/postlund/pyatv/blob/master/pyatv/interface.py#L864-L891" class="git-link">Browse git</a></div>
 <h3>Subclasses</h3>
 <ul class="hlist">
 <li>pyatv.core.facade.FacadeStream</li>
@@ -2074,7 +2079,7 @@ the storage.</p></section>
 </code></dt>
 <dd>
 <section class="desc"><p>Base class for touch gestures.</p></section>
-<div class="git-link-div"><a href="https://github.com/postlund/pyatv/blob/master/pyatv/interface.py#L1255-L1291" class="git-link">Browse git</a></div>
+<div class="git-link-div"><a href="https://github.com/postlund/pyatv/blob/master/pyatv/interface.py#L1268-L1304" class="git-link">Browse git</a></div>
 <h3>Ancestors</h3>
 <ul class="hlist">
 <li>abc.ABC</li>
@@ -2142,7 +2147,7 @@ in a given time (in milliseconds).</p>
 <dd>
 <section class="desc"><p>Information about a user account.</p>
 <p>Initialize a new UserAccount instance.</p></section>
-<div class="git-link-div"><a href="https://github.com/postlund/pyatv/blob/master/pyatv/interface.py#L723-L749" class="git-link">Browse git</a></div>
+<div class="git-link-div"><a href="https://github.com/postlund/pyatv/blob/master/pyatv/interface.py#L736-L762" class="git-link">Browse git</a></div>
 <h3>Instance variables</h3>
 <dl>
 <dt id="pyatv.interface.UserAccount.identifier"><code class="name">var <span class="ident">identifier</span> -> str</code></dt>
@@ -2160,7 +2165,7 @@ in a given time (in milliseconds).</p>
 </code></dt>
 <dd>
 <section class="desc"><p>Base class for account handling.</p></section>
-<div class="git-link-div"><a href="https://github.com/postlund/pyatv/blob/master/pyatv/interface.py#L752-L763" class="git-link">Browse git</a></div>
+<div class="git-link-div"><a href="https://github.com/postlund/pyatv/blob/master/pyatv/interface.py#L765-L776" class="git-link">Browse git</a></div>
 <h3>Subclasses</h3>
 <ul class="hlist">
 <li>pyatv.core.facade.FacadeUserAccounts</li>
diff --git a/pyatv/const.py b/pyatv/const.py
index e232fa3f..c10383c7 100644
--- a/pyatv/const.py
+++ b/pyatv/const.py
@@ -363,6 +363,9 @@ class FeatureName(Enum):
     ContentIdentifier = 47
     """Identifier for Content"""
 
+    iTunesStoreIdentifier = 50
+    """iTunes Store Identifier for Content"""
+
     AppList = 38
     """List of launchable apps."""
 
diff --git a/pyatv/interface.py b/pyatv/interface.py
index 336bf4a1..dae282b2 100644
--- a/pyatv/interface.py
+++ b/pyatv/interface.py
@@ -475,6 +475,7 @@ class Playing(ABC):
         "season_number",
         "episode_number",
         "content_identifier",
+        "itunes_store_identifier",
     ]
 
     def __init__(  # pylint: disable=too-many-locals
@@ -494,8 +495,10 @@ class Playing(ABC):
         season_number: Optional[int] = None,
         episode_number: Optional[int] = None,
         content_identifier: Optional[str] = None,
+        itunes_store_identifier: Optional[int] = None,
     ) -> None:
         """Initialize a new Playing instance."""
+        self._itunes_store_identifier = None
         self._media_type = media_type
         self._device_state = device_state
         self._title = title
@@ -511,6 +514,8 @@ class Playing(ABC):
         self._season_number = season_number
         self._episode_number = episode_number
         self._content_identifier = content_identifier
+        self._itunes_store_identifier = itunes_store_identifier
+
         self._post_process()
 
     def _post_process(self) -> None:
@@ -570,6 +575,8 @@ class Playing(ABC):
         if self.shuffle is not None:
             output.append(f"     Shuffle: {convert.shuffle_str(self.shuffle)}")
 
+        if self._itunes_store_identifier is not None:
+            output.append(f"iTunes Store Identifier: {self._itunes_store_identifier}")
         return "\n".join(output)
 
     def __eq__(self, other) -> bool:
@@ -676,6 +683,12 @@ class Playing(ABC):
         """Content identifier (app specific)."""
         return self._content_identifier
 
+    @property  # type: ignore
+    @feature(50, "iTunesStoreIdentifier", "iTunes Store identifier for Content")
+    def itunes_store_identifier(self) -> Optional[int]:
+        """Itunes Store identifier."""
+        return self._itunes_store_identifier
+
 
 class App:
     """Information about an app."""
diff --git a/pyatv/protocols/mrp/__init__.py b/pyatv/protocols/mrp/__init__.py
index 685d2dea..750f5c61 100644
--- a/pyatv/protocols/mrp/__init__.py
+++ b/pyatv/protocols/mrp/__init__.py
@@ -142,6 +142,7 @@ _FIELD_FEATURES: Dict[FeatureName, str] = {
     FeatureName.SeasonNumber: "seasonNumber",
     FeatureName.EpisodeNumber: "episodeNumber",
     FeatureName.ContentIdentifier: "contentIdentifier",
+    FeatureName.iTunesStoreIdentifier: "iTunesStoreIdentifier",
 }
 
 DELAY_BETWEEN_COMMANDS = 0.1
@@ -267,6 +268,10 @@ def build_playing_instance(  # pylint: disable=too-many-locals
         """Content identifier."""
         return state.metadata_field("contentIdentifier")
 
+    def itunes_store_identifier() -> int:
+        """Itunes Store identifier."""
+        return state.metadata_field("iTunesStoreIdentifier")
+
     return Playing(
         media_type=media_type(),
         device_state=device_state(),
@@ -283,6 +288,7 @@ def build_playing_instance(  # pylint: disable=too-many-locals
         season_number=season_number(),
         episode_number=episode_number(),
         content_identifier=content_identifier(),
+        itunes_store_identifier=itunes_store_identifier(),
     )
 
 


Test patch:
diff --git a/tests/fake_device/mrp.py b/tests/fake_device/mrp.py
index 5458a266..7eef03e9 100644
--- a/tests/fake_device/mrp.py
+++ b/tests/fake_device/mrp.py
@@ -118,6 +118,8 @@ def _fill_item(item, metadata):
         md.episodeNumber = metadata.episode_number
     if metadata.content_identifier:
         md.contentIdentifier = metadata.content_identifier
+    if metadata.itunes_store_identifier:
+        md.iTunesStoreIdentifier = metadata.itunes_store_identifier
 
 
 def _set_state_message(metadata, identifier):
@@ -194,6 +196,7 @@ class PlayingState:
         self.skip_time = kwargs.get("skip_time")
         self.app_name = kwargs.get("app_name")
         self.content_identifier = kwargs.get("content_identifier")
+        self.itunes_store_identifier = kwargs.get("itunes_store_identifier")
 
 
 class FakeMrpState:
diff --git a/tests/protocols/mrp/test_mrp_functional.py b/tests/protocols/mrp/test_mrp_functional.py
index d337a9f2..9082a96b 100644
--- a/tests/protocols/mrp/test_mrp_functional.py
+++ b/tests/protocols/mrp/test_mrp_functional.py
@@ -542,6 +542,7 @@ class MRPFunctionalTest(common_functional_tests.CommonFunctionalTests):
             season_number=12,
             episode_number=4,
             content_identifier="identifier",
+            itunes_store_identifier=123456789,
         )
 
         with faketime("pyatv", 0):
@@ -554,6 +555,7 @@ class MRPFunctionalTest(common_functional_tests.CommonFunctionalTests):
             self.assertEqual(playing.season_number, 12)
             self.assertEqual(playing.episode_number, 4)
             self.assertEqual(playing.content_identifier, "identifier")
+            self.assertEqual(playing.itunes_store_identifier, 123456789)
 
         self.assertFeatures(
             FeatureState.Available,
diff --git a/tests/scripts/test_atvscript.py b/tests/scripts/test_atvscript.py
index d083783c..f2be15ef 100644
--- a/tests/scripts/test_atvscript.py
+++ b/tests/scripts/test_atvscript.py
@@ -103,6 +103,7 @@ async def test_mrp_idle(scriptenv):
             "season_number": None,
             "episode_number": None,
             "content_identifier": None,
+            "itunes_store_identifier": None,
         },
     )
     assert exit_code == 0
diff --git a/tests/test_interface.py b/tests/test_interface.py
index 3be339c7..a99d12d6 100644
--- a/tests/test_interface.py
+++ b/tests/test_interface.py
@@ -128,6 +128,7 @@ def test_playing_basic_fields():
             season_number=1245,
             episode_number=2468,
             content_identifier="content_id",
+            itunes_store_identifier=123456789,
         )
     )
     assert "mytitle" in out
@@ -138,6 +139,7 @@ def test_playing_basic_fields():
     assert "1245" in out
     assert "2468" in out
     assert "content_id" in out
+    assert "123456789" in out
 
 
 @pytest.mark.parametrize(
@@ -197,7 +199,7 @@ def test_playing_custom_hash():
 def test_playing_eq_ensure_member_count():
     # Fail if a property is added or removed to interface, just as a reminder to
     # update equality comparison
-    assert len(Playing().__dict__) == 15
+    assert len(Playing().__dict__) == 16
 
 
 @pytest.mark.parametrize(