Separated image.catalog.zune.net handling from catalog.zune.net

This commit is contained in:
Joshua Askharoun
2021-04-19 13:46:23 -05:00
parent 9abe7717a0
commit b4e9b09ef9
7 changed files with 10 additions and 3817 deletions
+2 -2
View File
@@ -1,6 +1,6 @@
# PyZuneCatalogServer
# PyZuneImageCatalogServer
This repository houses a community effort to recreate the Zune Marketplace servers.
This repository houses a community effort to recreate the Zune Marketplace image servers.
## Contributing
+8 -368
View File
@@ -1,380 +1,20 @@
from xml.dom.minidom import Document, Element
from flask import Flask, request, Response
import musicbrainzngs
from atom.factory import *
import urllib.request
from locale import *
locale = getdefaultlocale()[0]
app = Flask(__name__)
musicbrainzngs.set_useragent("Zune", "4.8", "https://github.com/yoshiask/PyZuneCatalogServer")
caa_supported_sizes = [250, 500, 1200]
@app.route(f"/v3.2/<string:locale>/hubs/music")
def hubs_music(locale: str):
with open('reference/catalog.zune.net_v3.2_{locale}_hubs_music.xml', 'r') as file:
data: str = file.read().replace('\n', '')
return Response(data, mimetype=MIME_ATOM_XML)
@app.route(f"/v3.2/<string:locale>/music/hub/podcast/")
def hubs_podcasts(locale: str):
with open('reference/catalog.zune.net_v3.2_{locale}_music_hub_podcast.xml', 'r') as file:
data: str = file.read().replace('\n', '')
return Response(data, mimetype=MIME_ATOM_XML)
@app.route(f"/v3.2/<string:locale>/music/genre/<string:id>/")
def music_genre_details(id: str, locale: str):
with open('reference/catalog.zune.net_v3.2_{locale}_music_genre.xml', 'r') as file:
data: str = file.read().replace('\n', '')
return Response(data, mimetype=MIME_ATOM_XML)
@app.route(f"/v3.2/<string:locale>/music/genre/<string:id>/albums/")
def music_genre_albums(id: str, locale: str):
with open('reference/catalog.zune.net_v3.2_{locale}_music_genre.xml', 'r') as file:
data: str = file.read().replace('\n', '')
return Response(data, mimetype=MIME_ATOM_XML)
@app.route(f"/v3.2/<string:locale>/music/genre/")
def music_genre(locale: str):
from musizbrainz.genrelist import GENRES
lastpulledlist: datetime = datetime(2021, 2, 21)
doc: Document = minidom.Document()
feed: Element = create_feed(doc, "Genres", "genre", request.endpoint, lastpulledlist)
for genre_name, genre_id in GENRES.items():
entry: Element = create_entry(doc, genre_name, genre_id, "/v3.2/{locale}/music/genre" + genre_id, lastpulledlist)
feed.appendChild(entry)
xml_str = doc.toprettyxml(indent="\t")
return Response(xml_str, mimetype=MIME_XML)
@app.route(f"/v3.2/<string:locale>/music/album/<album_id>/")
def music_get_album(album_id: str, locale: str):
album = musicbrainzngs.get_release_by_id(album_id, includes=["artists", "recordings"])["release"]
album_name: str = album["title"]
artist = album["artist-credit"][0]["artist"]
artist_id: str = artist["id"]
artist_name: str = artist["name"]
tracks = album["medium-list"][0]["track-list"]
doc: Document = minidom.Document()
feed: Element = create_feed(doc, album_name, album_id, request.endpoint)
if bool(album["cover-art-archive"]["front"]):
# Add front cover
image_elem: Element = doc.createElement("image")
image_id_elem: Element = create_id(doc, album_id)
image_elem.appendChild(image_id_elem)
feed.appendChild(image_elem)
for track in tracks:
recording = track["recording"]
track_id: str = recording["id"]
track_title: str = recording["title"]
entry: Element = create_entry(doc, track_title, track_id, f"/v3.2/{locale}/")
# Create primaryArtist element
primary_artist_elem: Element = doc.createElement("primaryArtist")
artist_id_element: Element = doc.createElement("id")
set_element_value(artist_id_element, artist_id)
primary_artist_elem.appendChild(artist_id_element)
artist_name_element: Element = doc.createElement("name")
set_element_value(artist_name_element, artist_name)
primary_artist_elem.appendChild(artist_name_element)
entry.appendChild(primary_artist_elem)
try:
length_ms: str = track["track_or_recording_length"]
# FIXME: Add duration element
length_elem: Element = doc.createElement("duration")
set_element_value(length_elem, length_ms)
except:
pass
try:
track_position: str = track["position"]
# FIXME: Add index element
index_elem: Element = doc.createElement("index")
set_element_value(index_elem, track_position)
except:
pass
feed.appendChild(entry)
# doc.appendChild(feed)
xml_str = doc.toprettyxml(indent="\t")
return Response(xml_str, mimetype=MIME_XML)
@app.route(f"/v3.2/<string:locale>/music/album/<string:id>/<path:fragment>/")
def music_album_details(id: str, fragment: str, locale: str):
return fragment
# Get artist information
@app.route(f"/v3.2/<string:locale>/music/artist/<string:id>/")
def music_get_artist(id: str, locale: str):
return music_get_artist_tracks(id, locale)
# Get artist's tracks
@app.route(f"/v3.2/<string:locale>/music/artist/<string:artist_id>/tracks/")
def music_get_artist_tracks(artist_id: str, locale: str):
recordings = musicbrainzngs.browse_recordings(artist_id, limit=100)["recording-list"]
artist = musicbrainzngs.get_artist_by_id(artist_id)["artist"]
artist_name: str = artist["name"]
doc: Document = minidom.Document()
feed: Element = create_feed(doc, artist_id, artist_name, request.endpoint)
for recording in recordings:
id: str = recording["id"]
title: str = recording["title"]
entry: Element = create_entry(doc, title, id, f"/v3.2/{locale}/music/artist/{artist_id}/tracks/{id}")
feed.appendChild(entry)
# Create primaryArtist element
primary_artist_elem: Element = doc.createElement("primaryArtist")
artist_id_element: Element = doc.createElement("id")
set_element_value(artist_id_element, artist_id)
primary_artist_elem.appendChild(artist_id_element)
artist_name_element: Element = doc.createElement("name")
set_element_value(artist_name_element, artist_name)
primary_artist_elem.appendChild(artist_name_element)
entry.appendChild(primary_artist_elem)
#doc.appendChild(feed)
xml_str = doc.toprettyxml(indent="\t")
return Response(xml_str, mimetype=MIME_XML)
# Get artist's albums
@app.route(f"/v3.2/<string:locale>/music/artist/<string:artist_id>/albums/")
def music_get_artist_albums(artist_id: str, locale: str):
releases = musicbrainzngs.browse_releases(artist_id, limit=100)["release-list"]
artist = musicbrainzngs.get_artist_by_id(artist_id)["artist"]
artist_name: str = artist["name"]
doc: Document = minidom.Document()
feed: Element = create_feed(doc, artist_id, artist_name, request.endpoint)
for release in releases:
id: str = release["id"]
title: str = release["title"]
entry: Element = create_entry(doc, title, id, f"/v3.2/{locale}/music/artist/{artist_id}/albums/{id}")
feed.appendChild(entry)
# Add front cover
image_elem: Element = doc.createElement("image")
image_id_elem: Element = create_id(doc, id)
image_elem.appendChild(image_id_elem)
entry.appendChild(image_elem)
# Create primaryArtist element
primary_artist_elem: Element = doc.createElement("primaryArtist")
artist_id_element: Element = doc.createElement("id")
set_element_value(artist_id_element, artist_id)
primary_artist_elem.appendChild(artist_id_element)
artist_name_element: Element = doc.createElement("name")
set_element_value(artist_name_element, artist_name)
primary_artist_elem.appendChild(artist_name_element)
entry.appendChild(primary_artist_elem)
#doc.appendChild(feed)
xml_str = doc.toprettyxml(indent="\t")
return Response(xml_str, mimetype=MIME_XML)
@app.route(f"/v3.2/<string:locale>/music/artist/<string:id>/<path:fragment>/")
def music_artist_details(id: str, fragment: str, locale: str):
return fragment
# Get top tracks
@app.route(f"/v3.2/<string:locale>/music/chart/zune/tracks/")
def music_chart_tracks(locale: str):
recordings = musicbrainzngs.search_recordings("*", limit=100)
doc: Document = minidom.Document()
feed: Element = create_feed(doc, "tracks", "tracks", f"/v3.2/{locale}/music/chart/zune/tracks")
for recording in recordings["recording-list"]:
# Set track ID and Title
id: str = recording["id"]
title: str = recording["title"]
entry: Element = create_entry(doc, title, id, f"/v3.2/{locale}/music/collection/features/" + id)
# Get artist ID and Name
artist = recording["artist-credit"][0]["artist"]
artist_id: str = artist["id"]
artist_name: str = artist["name"]
# Create primaryArtist element
primary_artist_elem: Element = doc.createElement("primaryArtist")
artist_id_element: Element = doc.createElement("id")
set_element_value(artist_id_element, artist_id)
primary_artist_elem.appendChild(artist_id_element)
artist_name_element: Element = doc.createElement("name")
set_element_value(artist_name_element, artist_name)
primary_artist_elem.appendChild(artist_name_element)
entry.appendChild(primary_artist_elem)
feed.appendChild(entry)
xml_str = doc.toprettyxml(indent="\t")
return Response(xml_str, mimetype=MIME_XML)
# Search tracks
@app.route(f"/v3.2/<string:locale>/music/track")
def music_track(locale: str):
query: str = request.args.get("q")
response = musicbrainzngs.search_recordings(query)
doc: Document = minidom.Document()
feed: Element = create_feed(doc, "tracks", "tracks", request.endpoint)
for recording in response["recording-list"]:
try:
# Set track ID and Title
id: str = recording["id"]
title: str = recording["title"]
entry: Element = create_entry(doc, title, id, f"/v3.2/{locale}/music/track/" + id)
# Get artist ID and Name
artist = recording["artist-credit"][0]["artist"]
artist_id: str = artist["id"]
artist_name: str = artist["name"]
# Create primaryArtist element
primary_artist_elem: Element = doc.createElement("primaryArtist")
artist_id_element: Element = doc.createElement("id")
set_element_value(artist_id_element, artist_id)
primary_artist_elem.appendChild(artist_id_element)
artist_name_element: Element = doc.createElement("name")
set_element_value(artist_name_element, artist_name)
primary_artist_elem.appendChild(artist_name_element)
entry.appendChild(primary_artist_elem)
# Get album ID and Title
album = recording["release-list"][0]
album_id: str = album["id"]
album_name: str = album["title"]
# Create album element
album_elem: Element = doc.createElement("album")
album_id_element: Element = doc.createElement("id")
set_element_value(album_id_element, album_id)
album_elem.appendChild(album_id_element)
album_name_element: Element = doc.createElement("title")
set_element_value(album_name_element, album_name)
album_elem.appendChild(album_name_element)
entry.appendChild(album_elem)
feed.appendChild(entry)
except:
pass
xml_str = doc.toprettyxml(indent="\t")
return Response(xml_str, mimetype=MIME_XML)
# Search albums
@app.route(f"/v3.2/<string:locale>/music/album")
def music_album(locale: str):
query: str = request.args.get("q")
response = musicbrainzngs.search_releases(query)
doc: Document = minidom.Document()
feed: Element = create_feed(doc, "Albums", "albums", f"/v3.2/{locale}/music/chart/zune/albums")
for release in response["release-list"]:
print(release)
# Set track ID and Title
id: str = release["id"]
title: str = release["title"]
entry: Element = create_entry(doc, title, id, f"/v3.2/{locale}/music/album/" + id)
# Add front cover
image_elem: Element = doc.createElement("image")
image_id_elem: Element = create_id(doc, id)
image_elem.appendChild(image_id_elem)
entry.appendChild(image_elem)
# Get artist ID and Name
artist = release["artist-credit"][0]["artist"]
artist_id: str = artist["id"]
artist_name: str = artist["name"]
# Create primaryArtist element
primary_artist_elem: Element = doc.createElement("primaryArtist")
artist_id_element: Element = doc.createElement("id")
set_element_value(artist_id_element, artist_id)
primary_artist_elem.appendChild(artist_id_element)
artist_name_element: Element = doc.createElement("name")
set_element_value(artist_name_element, artist_name)
primary_artist_elem.appendChild(artist_name_element)
entry.appendChild(primary_artist_elem)
feed.appendChild(entry)
xml_str = doc.toprettyxml(indent="\t")
return Response(xml_str, mimetype=MIME_XML)
@app.route("/")
def commerce():
# TODO: Attempt SSL handshake
return "Howdy, Zune!"
### MOVIES
@app.route(f"/v3.2/<string:locale>/chart/zuneDownload/movie/")
def chart_zunedown_movie(locale: str):
with open('reference/catalog.zune.net_v3.2_en-US_hubs_music.xml', 'r') as file:
data: str = file.read().replace('\n', '')
return Response(data, mimetype=MIME_ATOM_XML)
@app.route(f"/v3.2/<string:locale>/music/hub/movie/")
def hubs_movie(locale: str):
with open('reference/catalog.zune.net_v3.2_en-US_hubs_music.xml', 'r') as file:
data: str = file.read().replace('\n', '')
return Response(data, mimetype=MIME_ATOM_XML)
@app.route(f"/v3.2/<string:locale>/music/hub/video/")
def hubs_video(locale: str):
with open('reference/catalog.zune.net_v3.2_en-US_hubs_music.xml', 'r') as file:
data: str = file.read().replace('\n', '')
return Response(data, mimetype=MIME_ATOM_XML)
### IMAGES
@app.route(f"/v3.2/<string:locale>/image/<string:mbid>")
def get_image(mbid: str, locale: str):
print("Image request for", mbid)
import urllib.request
image = urllib.request.urlopen(f"http://coverartarchive.org/release/{mbid}/front")
return Response(image.read(), mimetype=MIME_JPG)
# The Cover Art Archive API supports sizes of 250, 500, and 1200
requested_width = request.args.get("width", default=500, type=int)
width = min(caa_supported_sizes, key=lambda x: abs(x - requested_width))
### Metadata
# fai.music.metaservices.microsoft.com
@app.route("/ZuneAPI/EndPoints.aspx")
def get_metadata_endpoints():
print(request.full_path)
with open('reference/catalog.zune.net_v3.2_en-US_hubs_music.xml', 'r') as file:
data: str = file.read().replace('\n', '')
return Response(data, mimetype=MIME_ATOM_XML)
# Request the image from the API and forward it to the Zune software
image = urllib.request.urlopen(f"http://coverartarchive.org/release/{mbid}/front-{width}")
return Response(image.read(), mimetype="image/jpeg")
-83
View File
@@ -1,83 +0,0 @@
from typing import List
from xml.dom import minidom
from xml.dom.minidom import Element, Document, Text
from datetime import datetime
MIME_XML = "text/xml"
MIME_ATOM_XML = "application/atom+xml"
MIME_UIX = "application/uix"
MIME_JPG = "image/jpeg"
def set_element_value(element: Element, value: str):
content = Text()
content.data = value
element.appendChild(content)
def create_feed(doc: Document, title: str, id: str, href: str, date_updated: datetime = datetime.today()) -> Element:
feed = create_empty_feed(doc)
feed.appendChild(create_link(doc, href))
feed.appendChild(create_updated(doc, date_updated))
feed.appendChild(create_title(doc, title))
feed.appendChild(create_id(doc, id))
return feed
def create_empty_feed(doc: Document) -> Element:
# Add namespaces
feed: Element = doc.createElement("a:feed")
feed.setAttribute("xmlns:a", "http://www.w3.org/2005/Atom")
feed.setAttribute("xmlns:os", "http://a9.com/-/spec/opensearch/1.1/")
feed.setAttribute("xmlns", "http://schemas.zune.net/catalog/music/2007/10")
doc.appendChild(feed)
return feed
def create_link(doc: Document, href: str, rel: str = "self", type: str = MIME_ATOM_XML) -> Element:
link: Element = doc.createElement("a:link")
link.setAttribute("rel", rel)
link.setAttribute("type", type)
link.setAttribute("href", href)
return link
def create_updated(doc: Document, date_updated: datetime = datetime.today()) -> Element:
updated: Element = doc.createElement("a:updated")
set_element_value(updated, date_updated.isoformat())
return updated
def create_title(doc: Document, title: str, type: str = "text") -> Element:
title_elem: Element = doc.createElement("a:title")
title_elem.setAttribute("type", type)
set_element_value(title_elem, title)
return title_elem
def create_id(doc: Document, id: str) -> Element:
id_elem: Element = doc.createElement("a:id")
set_element_value(id_elem, id)
return id_elem
def create_entry(doc: Document, title: str, id: str, href: str, date_updated: datetime = datetime.today()) -> Element:
entry: Element = doc.createElement("a:entry")
entry.appendChild(create_link(doc, href))
entry.appendChild(create_updated(doc, date_updated))
entry.appendChild(create_title(doc, title))
entry.appendChild(create_id(doc, id))
return entry
def create_author(doc: Document, name: str) -> Element:
author_elem: Element = doc.createElement("a:author")
author_name_elem: Element = doc.createElement("a:name")
set_element_value(author_name_elem, name)
author_elem.appendChild(author_name_elem)
return author_elem
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -1,39 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- http://catalog.zune.net/v3.2/en-US/music/genre/ -->
<a:feed xmlns:a="http://www.w3.org/2005/Atom"
xmlns:os="http://a9.com/-/spec/opensearch/1.1/"
xmlns="http://schemas.zune.net/catalog/music/2007/10">
<a:link rel="self" type="application/atom+xml" href="/v3.2/en-US/music/genre" />
<a:updated>2019-04-08T21:58:31.4152623Z</a:updated>
<a:title type="text">Howdy</a:title>
<a:id>genre</a:id>
<a:entry>
<a:link rel="alternate" type="application/atom+xml" href="/v3.2/en-US/music/collection/features/43c33b95-2ee2-4432-b272-0b29190fec9b" />
<a:updated>2019-04-08T21:58:31.4152623Z</a:updated>
<a:title type="text">Howdy, Zune!</a:title>
<a:id>urn:uuid:43c33b95-2ee2-4432-b272-0b29190fec9b</a:id>
<primaryArtist>
<id>urn:uuid:37690e00-0600-11db-89ca-0019b92a3933</id>
<name>Love Infinity</name>
</primaryArtist>
<index>1</index>
</a:entry>
<a:entry>
<a:link rel="alternate" type="application/atom+xml" href="/v3.2/en-US/podcastCollections/de7cdcbd-5cb3-4c85-9be2-5dc491c4f070" />
<a:updated>2019-04-08T21:58:31.4152623Z</a:updated>
<a:title type="text">So, how's your day?</a:title>
<a:id>urn:uuid:de7cdcbd-5cb3-4c85-9be2-5dc491c4f070</a:id>
<index>2</index>
</a:entry>
<a:entry>
<a:link rel="alternate" type="application/atom+xml" href="/v3.2/en-US/podcastCollections/14d631d4-2289-4820-81f3-d372b3dcb940" />
<a:updated>2019-04-08T21:58:31.4152623Z</a:updated>
<a:title type="text">Test</a:title>
<a:id>urn:uuid:14d631d4-2289-4820-81f3-d372b3dcb940</a:id>
<index>3</index>
</a:entry>
<a:author>
<a:name>Yoshi Askharoun &amp; Musicbrainz</a:name>
</a:author>
</a:feed>
@@ -1,483 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- http://catalog.zune.net/v3.2/en-US/music/hub/podcast/ -->
<a:feed xmlns:a="http://www.w3.org/2005/Atom"
xmlns:os="http://a9.com/-/spec/opensearch/1.1/"
xmlns="http://schemas.zune.net/catalog/music/2007/10">
<a:link rel="self" type="application/atom+xml" href="/v3.2/en-US/music/hub/podcast" />
<a:updated>2019-04-08T21:58:31.4152623Z</a:updated>
<a:title type="text">podcast</a:title>
<a:id>podcast</a:id>
<templates>
<template>
<mimeType>application/uix</mimeType>
<templateName>PodcastHub</templateName>
</template>
</templates>
<a:entry>
<a:link rel="alternate" type="application/atom+xml" href="/v3.2/en-US/music/collection/features/43c33b95-2ee2-4432-b272-0b29190fec9b" />
<a:updated>2019-04-08T21:58:31.4152623Z</a:updated>
<a:title type="text">List Of Items</a:title>
<a:id>urn:uuid:43c33b95-2ee2-4432-b272-0b29190fec9b</a:id>
<index>1</index>
<link>
<type>FeatureCollection</type>
<target>43C33B95-2EE2-4432-B272-0B29190FEC9B</target>
</link>
<editorialItems>
<editorialItem>
<id>urn:uuid:5916d513-919d-482d-9ae6-96a112cfba57</id>
<link>
<type>Podcast</type>
<target>22eb6a56-e380-465f-b271-1868f2d18ce1</target>
</link>
<title>Adam Carolla (Audio)</title>
<text>Favorite Daily Podcast</text>
<sequenceNumber>1</sequenceNumber>
<image>
<id>urn:uuid:43195fea-d512-4620-978a-ea2cd799b8c0</id>
</image>
<backgroundImage>
<id>urn:uuid:43195fea-d512-4620-978a-ea2cd799b8c0</id>
</backgroundImage>
</editorialItem>
<editorialItem>
<id>urn:uuid:d51e0f24-619a-4948-ab1f-9974a2ec9537</id>
<link>
<type>Podcast</type>
<target>0a9ca497-cf8b-4c6f-b849-987606805aeb</target>
</link>
<title>The Rich Eisen Show (audio)</title>
<text>biggest names in sports and entertainment</text>
<sequenceNumber>2</sequenceNumber>
<image>
<id>urn:uuid:57edc7d4-12aa-464a-bfb0-654b4acbdd49</id>
</image>
<backgroundImage>
<id>urn:uuid:57edc7d4-12aa-464a-bfb0-654b4acbdd49</id>
</backgroundImage>
</editorialItem>
<editorialItem>
<id>urn:uuid:e9e0021a-61da-4863-880b-58f9c1437537</id>
<link>
<type>Podcast</type>
<target>eaa73fa8-7cb4-4efa-8821-92f8bbcb65d4</target>
</link>
<title>Next at Microsoft Podcast</title>
<text>tech talk with MS insiders</text>
<sequenceNumber>3</sequenceNumber>
<image>
<id>urn:uuid:2f84c939-4dbb-43fd-9e54-af7722e240ab</id>
</image>
<backgroundImage>
<id>urn:uuid:2f84c939-4dbb-43fd-9e54-af7722e240ab</id>
</backgroundImage>
</editorialItem>
<editorialItem>
<id>urn:uuid:7d761639-b4f2-4d08-bfc2-816810996c47</id>
<link>
<type>Podcast</type>
<target>9b3b8aa6-5d47-4c0e-bdc6-c9595408c1d7</target>
</link>
<title>The Daily Show Podcast without Jon Stewart (Audio)</title>
<text>behind the Scenes Podcast</text>
<sequenceNumber>4</sequenceNumber>
<image>
<id>urn:uuid:e550bad6-e0b0-4052-b758-b0ed13a1e5e7</id>
</image>
<backgroundImage>
<id>urn:uuid:e550bad6-e0b0-4052-b758-b0ed13a1e5e7</id>
</backgroundImage>
</editorialItem>
</editorialItems>
</a:entry>
<a:entry>
<a:link rel="alternate" type="application/atom+xml" href="/v3.2/en-US/podcastCollections/de7cdcbd-5cb3-4c85-9be2-5dc491c4f070" />
<a:updated>2019-04-08T21:58:31.4152623Z</a:updated>
<a:title type="text">List Of Items</a:title>
<a:id>urn:uuid:de7cdcbd-5cb3-4c85-9be2-5dc491c4f070</a:id>
<index>2</index>
<link>
<type>PodcastCollection</type>
<target>DE7CDCBD-5CB3-4C85-9BE2-5DC491C4F070</target>
</link>
<editorialItems>
<editorialItem>
<id>urn:uuid:b30a6a28-9eaf-4366-b1a6-7cd2940735ed</id>
<link>
<type>Podcast</type>
<target>842dfdc6-77a0-4d5e-a5d2-ecea34e80f01</target>
</link>
<title>Xbox Live's Major Nelson Radio</title>
<sequenceNumber>1</sequenceNumber>
<image>
<id>urn:uuid:342b3d0b-f15c-43df-ae51-dcad9dc82478</id>
</image>
<backgroundImage>
<id>urn:uuid:342b3d0b-f15c-43df-ae51-dcad9dc82478</id>
</backgroundImage>
</editorialItem>
<editorialItem>
<id>urn:uuid:85c03684-0df9-49f7-8524-93249f26ebdd</id>
<link>
<type>Podcast</type>
<target>e42667f5-a840-4772-a082-47367ae3e73c</target>
</link>
<title>NPR: TED Radio Hour Podcast</title>
<sequenceNumber>2</sequenceNumber>
<image>
<id>urn:uuid:acefcfba-f8d5-4cf3-9304-806b83852734</id>
</image>
<backgroundImage>
<id>urn:uuid:acefcfba-f8d5-4cf3-9304-806b83852734</id>
</backgroundImage>
</editorialItem>
<editorialItem>
<id>urn:uuid:947a4964-5da9-4122-9684-b968dd1ed9c5</id>
<link>
<type>Podcast</type>
<target>e3e5edc2-7d4b-4c6d-a15b-67216ccc3aea</target>
</link>
<title>Freakonomics Radio</title>
<sequenceNumber>3</sequenceNumber>
<image>
<id>urn:uuid:2a57c37f-c971-428e-922a-1c51badb330e</id>
</image>
<backgroundImage>
<id>urn:uuid:2a57c37f-c971-428e-922a-1c51badb330e</id>
</backgroundImage>
</editorialItem>
<editorialItem>
<id>urn:uuid:5dc845ba-1424-46c6-9583-8c5ff82a48ac</id>
<link>
<type>Podcast</type>
<target>426705bc-e72b-479c-b565-c9d229ae78fe</target>
</link>
<title>This American Life</title>
<sequenceNumber>4</sequenceNumber>
<image>
<id>urn:uuid:e4a138ca-17a2-45cb-8154-09fed072c135</id>
</image>
<backgroundImage>
<id>urn:uuid:e4a138ca-17a2-45cb-8154-09fed072c135</id>
</backgroundImage>
</editorialItem>
<editorialItem>
<id>urn:uuid:9f455d14-545c-4ad5-9562-ea5b4ea854bd</id>
<link>
<type>Podcast</type>
<target>607f7590-0398-4f6b-9c49-1a42cf12b9f7</target>
</link>
<title>99% Invisible</title>
<sequenceNumber>5</sequenceNumber>
<image>
<id>urn:uuid:4049a0f0-e7d6-49bf-8ece-5eb0cda548b3</id>
</image>
<backgroundImage>
<id>urn:uuid:4049a0f0-e7d6-49bf-8ece-5eb0cda548b3</id>
</backgroundImage>
</editorialItem>
<editorialItem>
<id>urn:uuid:e2455ac3-e025-46d9-940a-c7834ed7c74e</id>
<link>
<type>Podcast</type>
<target>7872bc0f-dfd9-4c26-b53c-2052bce67013</target>
</link>
<title>SModcast - SModcast.com</title>
<sequenceNumber>6</sequenceNumber>
<image>
<id>urn:uuid:8b9ec9ff-9d86-4fbf-9029-b1bd692de4b2</id>
</image>
<backgroundImage>
<id>urn:uuid:8b9ec9ff-9d86-4fbf-9029-b1bd692de4b2</id>
</backgroundImage>
</editorialItem>
<editorialItem>
<id>urn:uuid:70bf94f7-4bfb-4623-9e3c-3f88ecbc938f</id>
<link>
<type>Podcast</type>
<target>87a4de99-43f4-401f-857c-75deb66ae9cd</target>
</link>
<title>NPR: Fresh Air</title>
<sequenceNumber>7</sequenceNumber>
<image>
<id>urn:uuid:9a9977dc-5f70-4a11-8dd8-b9e51e7e4916</id>
</image>
<backgroundImage>
<id>urn:uuid:9a9977dc-5f70-4a11-8dd8-b9e51e7e4916</id>
</backgroundImage>
</editorialItem>
<editorialItem>
<id>urn:uuid:4121080c-cbdd-44aa-97e9-896d459b082f</id>
<link>
<type>Podcast</type>
<target>93041890-4231-491b-86a8-0e18fa9c24bd</target>
</link>
<title>TEDTalks (audio)</title>
<sequenceNumber>8</sequenceNumber>
<image>
<id>urn:uuid:abaac5b8-5fcd-407b-a177-25cc5ba6b843</id>
</image>
<backgroundImage>
<id>urn:uuid:abaac5b8-5fcd-407b-a177-25cc5ba6b843</id>
</backgroundImage>
</editorialItem>
<editorialItem>
<id>urn:uuid:5ebb312d-9463-4e8a-8ff7-88d5a6f1f466</id>
<link>
<type>Podcast</type>
<target>cf7f76aa-2425-4c6e-bd94-bc43bd7b6b69</target>
</link>
<title>NBC Meet the Press (audio)</title>
<sequenceNumber>9</sequenceNumber>
<image>
<id>urn:uuid:b91223eb-7812-405c-ba37-aa9b49756eb4</id>
</image>
<backgroundImage>
<id>urn:uuid:b91223eb-7812-405c-ba37-aa9b49756eb4</id>
</backgroundImage>
</editorialItem>
<editorialItem>
<id>urn:uuid:c3bcb819-9703-4e39-873a-8a70cd2b0a97</id>
<link>
<type>Podcast</type>
<target>3d7094df-ee44-40ad-938a-04fba9d1ddf8</target>
</link>
<title>Radiolab from WNYC (audio)</title>
<sequenceNumber>10</sequenceNumber>
<image>
<id>urn:uuid:58176823-e152-44db-9235-c8db4ad79192</id>
</image>
<backgroundImage>
<id>urn:uuid:58176823-e152-44db-9235-c8db4ad79192</id>
</backgroundImage>
</editorialItem>
<editorialItem>
<id>urn:uuid:e1e19a11-b8a4-4f90-a281-8348731d631e</id>
<link>
<type>Podcast</type>
<target>2bded9f1-d075-4c1c-9602-bdfd6bc23125</target>
</link>
<title>The Moth Podcast</title>
<sequenceNumber>11</sequenceNumber>
<image>
<id>urn:uuid:ade12061-cdb6-4908-91f4-c6f3c58b21d5</id>
</image>
<backgroundImage>
<id>urn:uuid:ade12061-cdb6-4908-91f4-c6f3c58b21d5</id>
</backgroundImage>
</editorialItem>
<editorialItem>
<id>urn:uuid:a1638ccf-2d52-4270-a87d-473cb1804667</id>
<link>
<type>Podcast</type>
<target>84dcdb28-dc9c-4008-b879-ab647e7005db</target>
</link>
<title>NPR: All Songs Considered Podcast</title>
<sequenceNumber>12</sequenceNumber>
<image>
<id>urn:uuid:56666975-3507-483e-a7a3-17561c80bf05</id>
</image>
<backgroundImage>
<id>urn:uuid:56666975-3507-483e-a7a3-17561c80bf05</id>
</backgroundImage>
</editorialItem>
</editorialItems>
</a:entry>
<a:entry>
<a:link rel="alternate" type="application/atom+xml" href="/v3.2/en-US/podcastCollections/14d631d4-2289-4820-81f3-d372b3dcb940" />
<a:updated>2019-04-08T21:58:31.4152623Z</a:updated>
<a:title type="text">List Of Items</a:title>
<a:id>urn:uuid:14d631d4-2289-4820-81f3-d372b3dcb940</a:id>
<index>3</index>
<link>
<type>PodcastCollection</type>
<target>14D631D4-2289-4820-81F3-D372B3DCB940</target>
</link>
<editorialItems>
<editorialItem>
<id>urn:uuid:59c13024-20b0-42a2-8dba-bcb83aaf1081</id>
<link>
<type>Podcast</type>
<target>eaa73fa8-7cb4-4efa-8821-92f8bbcb65d4</target>
</link>
<title>Next at Microsoft Podcast</title>
<sequenceNumber>1</sequenceNumber>
<image>
<id>urn:uuid:2f84c939-4dbb-43fd-9e54-af7722e240ab</id>
</image>
<backgroundImage>
<id>urn:uuid:2f84c939-4dbb-43fd-9e54-af7722e240ab</id>
</backgroundImage>
</editorialItem>
<editorialItem>
<id>urn:uuid:228b92fc-6589-48b1-80c2-662d22b49bdd</id>
<link>
<type>Podcast</type>
<target>04e1bf8b-9015-4004-b2dc-66f7f36bd85e</target>
</link>
<title>NPR: Wait Wait... Don't Tell Me! Podcast</title>
<sequenceNumber>2</sequenceNumber>
<image>
<id>urn:uuid:5d8a4e35-91eb-465d-a333-cbeabaf6c12a</id>
</image>
<backgroundImage>
<id>urn:uuid:5d8a4e35-91eb-465d-a333-cbeabaf6c12a</id>
</backgroundImage>
</editorialItem>
<editorialItem>
<id>urn:uuid:103b3495-2944-4cf7-a930-7f890c7a6183</id>
<link>
<type>Podcast</type>
<target>881fceab-78c1-49cc-9fa8-4c4f76c7fa90</target>
</link>
<title>Stuff You Should Know (Audio)</title>
<sequenceNumber>3</sequenceNumber>
<image>
<id>urn:uuid:03bfde94-685d-4859-b231-2b94f26dc275</id>
</image>
<backgroundImage>
<id>urn:uuid:03bfde94-685d-4859-b231-2b94f26dc275</id>
</backgroundImage>
</editorialItem>
<editorialItem>
<id>urn:uuid:ae024fc6-3e06-4906-8107-59e25c8ebbef</id>
<link>
<type>Podcast</type>
<target>65d16ac6-fbfa-48b4-8f36-afe69567121d</target>
</link>
<title>The Nerdist</title>
<sequenceNumber>4</sequenceNumber>
<image>
<id>urn:uuid:a70d8434-7d28-4dca-907f-e14bc499891a</id>
</image>
<backgroundImage>
<id>urn:uuid:a70d8434-7d28-4dca-907f-e14bc499891a</id>
</backgroundImage>
</editorialItem>
<editorialItem>
<id>urn:uuid:36e1c387-323a-4f3c-990b-d9a25240d02a</id>
<link>
<type>Podcast</type>
<target>3dc6c408-8753-4d02-94d5-8932bd0adf1a</target>
</link>
<title>Welcome to Night Vale</title>
<sequenceNumber>5</sequenceNumber>
<image>
<id>urn:uuid:b49c44d9-9f61-4f29-9fdb-92979953ef54</id>
</image>
<backgroundImage>
<id>urn:uuid:b49c44d9-9f61-4f29-9fdb-92979953ef54</id>
</backgroundImage>
</editorialItem>
<editorialItem>
<id>urn:uuid:4ce7fdda-2ef9-4db2-92a1-77fc67443704</id>
<link>
<type>Podcast</type>
<target>6cd78d46-d8ab-45aa-a04b-aaa139ff5ee1</target>
</link>
<title>The Cracked Podcast - Earwolf (Audio)</title>
<sequenceNumber>6</sequenceNumber>
<image>
<id>urn:uuid:573c8360-89cc-4718-adcb-0c5b80808ef9</id>
</image>
<backgroundImage>
<id>urn:uuid:573c8360-89cc-4718-adcb-0c5b80808ef9</id>
</backgroundImage>
</editorialItem>
<editorialItem>
<id>urn:uuid:1ae412d9-c3a5-4835-b5b5-d3ffcb38bc05</id>
<link>
<type>Podcast</type>
<target>6e161397-9b18-413d-b11f-a9453400db56</target>
</link>
<title>Serial</title>
<sequenceNumber>7</sequenceNumber>
<image>
<id>urn:uuid:4b61ebbe-fbf8-49bf-b754-6feb489a2d22</id>
</image>
<backgroundImage>
<id>urn:uuid:4b61ebbe-fbf8-49bf-b754-6feb489a2d22</id>
</backgroundImage>
</editorialItem>
<editorialItem>
<id>urn:uuid:44cc9616-ed49-4128-adb5-b24cb84af560</id>
<link>
<type>Podcast</type>
<target>63fb5bcb-821b-4b72-b217-597a595170ca</target>
</link>
<title>The Adam Carolla Show</title>
<sequenceNumber>8</sequenceNumber>
<image>
<id>urn:uuid:0f690b08-7cc8-4acc-9348-a6881f3c4d0a</id>
</image>
<backgroundImage>
<id>urn:uuid:0f690b08-7cc8-4acc-9348-a6881f3c4d0a</id>
</backgroundImage>
</editorialItem>
<editorialItem>
<id>urn:uuid:b8f7e682-9031-4af1-9775-df3df41785b1</id>
<link>
<type>Podcast</type>
<target>ab2fcb1e-67dc-46c7-b8b5-3fe4db504e8e</target>
</link>
<title>StarTalk Radio</title>
<sequenceNumber>9</sequenceNumber>
<image>
<id>urn:uuid:a37c599e-f6eb-4276-9b10-00ff379e0d12</id>
</image>
<backgroundImage>
<id>urn:uuid:a37c599e-f6eb-4276-9b10-00ff379e0d12</id>
</backgroundImage>
</editorialItem>
<editorialItem>
<id>urn:uuid:d4b4bd96-7cbf-4182-a439-8781ad6c19da</id>
<link>
<type>Podcast</type>
<target>6f2a8965-ca80-47da-81c1-e3b1d3664217</target>
</link>
<title>NPR: Planet Money Podcast</title>
<sequenceNumber>10</sequenceNumber>
<image>
<id>urn:uuid:a370c226-465e-455f-bcbf-9f0c47982b7b</id>
</image>
<backgroundImage>
<id>urn:uuid:a370c226-465e-455f-bcbf-9f0c47982b7b</id>
</backgroundImage>
</editorialItem>
<editorialItem>
<id>urn:uuid:16cede8e-da6b-4661-a94d-fc36473be59b</id>
<link>
<type>Podcast</type>
<target>889acb34-acb5-4326-a632-ec77b360825f</target>
</link>
<title>Criminal</title>
<sequenceNumber>11</sequenceNumber>
<image>
<id>urn:uuid:1783819c-1293-42bc-8859-03c51d0a84b2</id>
</image>
<backgroundImage>
<id>urn:uuid:1783819c-1293-42bc-8859-03c51d0a84b2</id>
</backgroundImage>
</editorialItem>
<editorialItem>
<id>urn:uuid:d79b2c3d-e29b-4669-8854-367defcdcd5d</id>
<link>
<type>Podcast</type>
<target>62c31b39-d21d-4b2b-888d-d9e7c2e9fb9e</target>
</link>
<title>It's Raining Cats and Dogs</title>
<sequenceNumber>12</sequenceNumber>
<image>
<id>urn:uuid:e64d7b49-e29c-4e5d-95c5-c8dc64453cff</id>
</image>
<backgroundImage>
<id>urn:uuid:e64d7b49-e29c-4e5d-95c5-c8dc64453cff</id>
</backgroundImage>
</editorialItem>
</editorialItems>
</a:entry>
<a:author>
<a:name>Microsoft Corporation</a:name>
</a:author>
</a:feed>