From ed6ae329b3d7b2ab50c3a2b358281c9ce203c460 Mon Sep 17 00:00:00 2001 From: TobiGr Date: Wed, 23 Dec 2020 00:07:37 +0100 Subject: [PATCH 01/10] [media.ccc.de] Add "recent" kiosk --- .../services/media_ccc/MediaCCCService.java | 25 +++--- .../extractors/MediaCCCRecentKiosk.java | 60 ++++++++++++++ .../MediaCCCRecentKioskExtractor.java | 79 +++++++++++++++++++ .../MediaCCCConferenceInfoItemExtractor.java | 2 +- .../MediaCCCRecentListLinkHandlerFactory.java | 24 ++++++ 5 files changed, 180 insertions(+), 10 deletions(-) create mode 100644 extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/extractors/MediaCCCRecentKiosk.java create mode 100644 extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/extractors/MediaCCCRecentKioskExtractor.java create mode 100644 extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/linkHandler/MediaCCCRecentListLinkHandlerFactory.java diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/MediaCCCService.java b/extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/MediaCCCService.java index 24dd20ba3..d3ed918a8 100644 --- a/extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/MediaCCCService.java +++ b/extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/MediaCCCService.java @@ -14,18 +14,14 @@ import org.schabi.newpipe.extractor.linkhandler.SearchQueryHandler; import org.schabi.newpipe.extractor.linkhandler.SearchQueryHandlerFactory; import org.schabi.newpipe.extractor.playlist.PlaylistExtractor; import org.schabi.newpipe.extractor.search.SearchExtractor; -import org.schabi.newpipe.extractor.services.media_ccc.extractors.MediaCCCConferenceExtractor; -import org.schabi.newpipe.extractor.services.media_ccc.extractors.MediaCCCConferenceKiosk; -import org.schabi.newpipe.extractor.services.media_ccc.extractors.MediaCCCSearchExtractor; -import org.schabi.newpipe.extractor.services.media_ccc.extractors.MediaCCCStreamExtractor; -import org.schabi.newpipe.extractor.services.media_ccc.linkHandler.MediaCCCConferenceLinkHandlerFactory; -import org.schabi.newpipe.extractor.services.media_ccc.linkHandler.MediaCCCConferencesListLinkHandlerFactory; -import org.schabi.newpipe.extractor.services.media_ccc.linkHandler.MediaCCCSearchQueryHandlerFactory; -import org.schabi.newpipe.extractor.services.media_ccc.linkHandler.MediaCCCStreamLinkHandlerFactory; +import org.schabi.newpipe.extractor.services.media_ccc.extractors.*; +import org.schabi.newpipe.extractor.services.media_ccc.linkHandler.*; import org.schabi.newpipe.extractor.stream.StreamExtractor; import org.schabi.newpipe.extractor.subscription.SubscriptionExtractor; import org.schabi.newpipe.extractor.suggestion.SuggestionExtractor; +import java.io.IOException; + import static java.util.Arrays.asList; import static org.schabi.newpipe.extractor.StreamingService.ServiceInfo.MediaCapability.AUDIO; import static org.schabi.newpipe.extractor.StreamingService.ServiceInfo.MediaCapability.VIDEO; @@ -95,7 +91,18 @@ public class MediaCCCService extends StreamingService { new MediaCCCConferencesListLinkHandlerFactory().fromUrl(url), kioskId); } }, new MediaCCCConferencesListLinkHandlerFactory(), "conferences"); - list.setDefaultKiosk("conferences"); + + list.addKioskEntry(new KioskList.KioskExtractorFactory() { + @Override + public KioskExtractor createNewKiosk(final StreamingService streamingService, + final String url, final String kioskId) + throws ExtractionException { + return new MediaCCCRecentKiosk(MediaCCCService.this, + new MediaCCCRecentListLinkHandlerFactory().fromUrl(url), kioskId); + } + }, new MediaCCCRecentListLinkHandlerFactory(), "recent"); + + list.setDefaultKiosk("recent"); } catch (Exception e) { throw new ExtractionException(e); } diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/extractors/MediaCCCRecentKiosk.java b/extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/extractors/MediaCCCRecentKiosk.java new file mode 100644 index 000000000..2656f7d64 --- /dev/null +++ b/extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/extractors/MediaCCCRecentKiosk.java @@ -0,0 +1,60 @@ +package org.schabi.newpipe.extractor.services.media_ccc.extractors; + +import com.grack.nanojson.JsonArray; +import com.grack.nanojson.JsonObject; +import com.grack.nanojson.JsonParser; +import com.grack.nanojson.JsonParserException; +import org.schabi.newpipe.extractor.Page; +import org.schabi.newpipe.extractor.StreamingService; +import org.schabi.newpipe.extractor.downloader.Downloader; +import org.schabi.newpipe.extractor.exceptions.ExtractionException; +import org.schabi.newpipe.extractor.exceptions.ParsingException; +import org.schabi.newpipe.extractor.kiosk.KioskExtractor; +import org.schabi.newpipe.extractor.linkhandler.ListLinkHandler; +import org.schabi.newpipe.extractor.stream.StreamInfoItem; +import org.schabi.newpipe.extractor.stream.StreamInfoItemsCollector; + +import javax.annotation.Nonnull; +import java.io.IOException; + +public class MediaCCCRecentKiosk extends KioskExtractor { + + private JsonObject doc; + + public MediaCCCRecentKiosk(StreamingService streamingService, ListLinkHandler linkHandler, String kioskId) { + super(streamingService, linkHandler, kioskId); + } + + @Override + public void onFetchPage(@Nonnull Downloader downloader) throws IOException, ExtractionException { + final String site = downloader.get("https://api.media.ccc.de/public/events/recent", + getExtractorLocalization()).responseBody(); + try { + doc = JsonParser.object().from(site); + } catch (JsonParserException jpe) { + throw new ExtractionException("Could not parse json.", jpe); + } + } + + @Nonnull + @Override + public InfoItemsPage getInitialPage() throws IOException, ExtractionException { + final JsonArray events = doc.getArray("events"); + StreamInfoItemsCollector collector = new StreamInfoItemsCollector(getServiceId()); + for (int i = 0; i < events.size(); i++) { + collector.commit(new MediaCCCRecentKioskExtractor(events.getObject(i))); + } + return new InfoItemsPage<>(collector, null); + } + + @Override + public InfoItemsPage getPage(Page page) throws IOException, ExtractionException { + return InfoItemsPage.emptyPage(); + } + + @Nonnull + @Override + public String getName() throws ParsingException { + return "recent"; + } +} diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/extractors/MediaCCCRecentKioskExtractor.java b/extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/extractors/MediaCCCRecentKioskExtractor.java new file mode 100644 index 000000000..77c7b1df7 --- /dev/null +++ b/extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/extractors/MediaCCCRecentKioskExtractor.java @@ -0,0 +1,79 @@ +package org.schabi.newpipe.extractor.services.media_ccc.extractors; + +import com.grack.nanojson.JsonObject; +import org.schabi.newpipe.extractor.exceptions.ParsingException; +import org.schabi.newpipe.extractor.localization.DateWrapper; +import org.schabi.newpipe.extractor.stream.StreamInfoItemExtractor; +import org.schabi.newpipe.extractor.stream.StreamType; + +import javax.annotation.Nullable; +import java.time.ZonedDateTime; +import java.time.format.DateTimeFormatter; + +public class MediaCCCRecentKioskExtractor implements StreamInfoItemExtractor { + + private final JsonObject event; + + public MediaCCCRecentKioskExtractor(final JsonObject event) { + this.event = event; + } + + @Override + public String getName() throws ParsingException { + return event.getString("title"); + } + + @Override + public String getUrl() throws ParsingException { + return event.getString("frontend_link"); + } + + @Override + public String getThumbnailUrl() throws ParsingException { + return event.getString("thumb_url"); + } + + @Override + public StreamType getStreamType() throws ParsingException { + return StreamType.VIDEO_STREAM; + } + + @Override + public boolean isAd() throws ParsingException { + return false; + } + + @Override + public long getDuration() throws ParsingException { + return event.getInt("duration"); + } + + @Override + public long getViewCount() throws ParsingException { + return event.getInt("view_count"); + } + + @Override + public String getUploaderName() throws ParsingException { + return ""; + } + + @Override + public String getUploaderUrl() throws ParsingException { + return event.getString("conference_url"); + } + + @Nullable + @Override + public String getTextualUploadDate() throws ParsingException { + return event.getString("date"); + } + + @Nullable + @Override + public DateWrapper getUploadDate() throws ParsingException { + final ZonedDateTime zonedDateTime = ZonedDateTime.parse(event.getString("date"), + DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSzzzz")); + return new DateWrapper(zonedDateTime.toOffsetDateTime(), false); + } +} diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/extractors/infoItems/MediaCCCConferenceInfoItemExtractor.java b/extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/extractors/infoItems/MediaCCCConferenceInfoItemExtractor.java index cea663edb..3ced44d9a 100644 --- a/extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/extractors/infoItems/MediaCCCConferenceInfoItemExtractor.java +++ b/extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/extractors/infoItems/MediaCCCConferenceInfoItemExtractor.java @@ -7,7 +7,7 @@ import org.schabi.newpipe.extractor.channel.ChannelInfoItemExtractor; import org.schabi.newpipe.extractor.exceptions.ParsingException; public class MediaCCCConferenceInfoItemExtractor implements ChannelInfoItemExtractor { - private JsonObject conference; + private final JsonObject conference; public MediaCCCConferenceInfoItemExtractor(final JsonObject conference) { this.conference = conference; diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/linkHandler/MediaCCCRecentListLinkHandlerFactory.java b/extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/linkHandler/MediaCCCRecentListLinkHandlerFactory.java new file mode 100644 index 000000000..8f9408ad9 --- /dev/null +++ b/extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/linkHandler/MediaCCCRecentListLinkHandlerFactory.java @@ -0,0 +1,24 @@ +package org.schabi.newpipe.extractor.services.media_ccc.linkHandler; + +import org.schabi.newpipe.extractor.linkhandler.ListLinkHandlerFactory; + +import java.util.List; +import java.util.regex.Pattern; + +public class MediaCCCRecentListLinkHandlerFactory extends ListLinkHandlerFactory { + @Override + public String getId(String url) { + return "recent"; + } + + @Override + public boolean onAcceptUrl(String url) { + final String pattern = "^(https?://)?media.ccc.de/recent/?$"; + return Pattern.matches(pattern, url); + } + + @Override + public String getUrl(String id, List contentFilter, String sortFilter) { + return "https://media.ccc.de/recent"; + } +} From b2154c6e873cc6352e26e1cc0c110af8a3123166 Mon Sep 17 00:00:00 2001 From: TobiGr Date: Fri, 25 Dec 2020 23:55:13 +0100 Subject: [PATCH 02/10] [media.ccc.de] Fix conferences kiosk test --- .../services/media_ccc/MediaCCCConferenceListExtractorTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extractor/src/test/java/org/schabi/newpipe/extractor/services/media_ccc/MediaCCCConferenceListExtractorTest.java b/extractor/src/test/java/org/schabi/newpipe/extractor/services/media_ccc/MediaCCCConferenceListExtractorTest.java index 90ebf0289..b35e59676 100644 --- a/extractor/src/test/java/org/schabi/newpipe/extractor/services/media_ccc/MediaCCCConferenceListExtractorTest.java +++ b/extractor/src/test/java/org/schabi/newpipe/extractor/services/media_ccc/MediaCCCConferenceListExtractorTest.java @@ -24,7 +24,7 @@ public class MediaCCCConferenceListExtractorTest { @BeforeClass public static void setUpClass() throws Exception { NewPipe.init(DownloaderTestImpl.getInstance()); - extractor = MediaCCC.getKioskList().getDefaultKioskExtractor(); + extractor = MediaCCC.getKioskList().getExtractorById("conferences", null); extractor.fetchPage(); } From 41699798fe83093661cbcb42dab78937b14f06fe Mon Sep 17 00:00:00 2001 From: TobiGr Date: Sat, 26 Dec 2020 12:47:28 +0100 Subject: [PATCH 03/10] [media.ccc.de] Use web URL for uploaderUrl instead of API URL in recent --- .../media_ccc/extractors/MediaCCCRecentKioskExtractor.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/extractors/MediaCCCRecentKioskExtractor.java b/extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/extractors/MediaCCCRecentKioskExtractor.java index 77c7b1df7..724b3e06b 100644 --- a/extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/extractors/MediaCCCRecentKioskExtractor.java +++ b/extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/extractors/MediaCCCRecentKioskExtractor.java @@ -3,6 +3,7 @@ package org.schabi.newpipe.extractor.services.media_ccc.extractors; import com.grack.nanojson.JsonObject; import org.schabi.newpipe.extractor.exceptions.ParsingException; import org.schabi.newpipe.extractor.localization.DateWrapper; +import org.schabi.newpipe.extractor.services.media_ccc.linkHandler.MediaCCCConferenceLinkHandlerFactory; import org.schabi.newpipe.extractor.stream.StreamInfoItemExtractor; import org.schabi.newpipe.extractor.stream.StreamType; @@ -39,7 +40,7 @@ public class MediaCCCRecentKioskExtractor implements StreamInfoItemExtractor { } @Override - public boolean isAd() throws ParsingException { + public boolean isAd() { return false; } @@ -60,7 +61,9 @@ public class MediaCCCRecentKioskExtractor implements StreamInfoItemExtractor { @Override public String getUploaderUrl() throws ParsingException { - return event.getString("conference_url"); + return new MediaCCCConferenceLinkHandlerFactory() + .fromUrl(event.getString("conference_url")) // API URL + .getUrl(); // web URL } @Nullable From d1a0686d2ffd3eedf0f503fc7c63ca8bd943d8e5 Mon Sep 17 00:00:00 2001 From: TobiGr Date: Sat, 26 Dec 2020 13:15:03 +0100 Subject: [PATCH 04/10] [media.ccc.de] Get conference name from API URL --- .../extractors/MediaCCCRecentKiosk.java | 10 ++++-- .../MediaCCCRecentKioskExtractor.java | 36 +++++++++++++++++-- 2 files changed, 41 insertions(+), 5 deletions(-) diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/extractors/MediaCCCRecentKiosk.java b/extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/extractors/MediaCCCRecentKiosk.java index 2656f7d64..91da8976e 100644 --- a/extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/extractors/MediaCCCRecentKiosk.java +++ b/extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/extractors/MediaCCCRecentKiosk.java @@ -4,6 +4,7 @@ import com.grack.nanojson.JsonArray; import com.grack.nanojson.JsonObject; import com.grack.nanojson.JsonParser; import com.grack.nanojson.JsonParserException; + import org.schabi.newpipe.extractor.Page; import org.schabi.newpipe.extractor.StreamingService; import org.schabi.newpipe.extractor.downloader.Downloader; @@ -14,12 +15,16 @@ import org.schabi.newpipe.extractor.linkhandler.ListLinkHandler; import org.schabi.newpipe.extractor.stream.StreamInfoItem; import org.schabi.newpipe.extractor.stream.StreamInfoItemsCollector; -import javax.annotation.Nonnull; import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +import javax.annotation.Nonnull; public class MediaCCCRecentKiosk extends KioskExtractor { private JsonObject doc; + private final Map conferenceNames = new HashMap<>(); public MediaCCCRecentKiosk(StreamingService streamingService, ListLinkHandler linkHandler, String kioskId) { super(streamingService, linkHandler, kioskId); @@ -41,8 +46,9 @@ public class MediaCCCRecentKiosk extends KioskExtractor { public InfoItemsPage getInitialPage() throws IOException, ExtractionException { final JsonArray events = doc.getArray("events"); StreamInfoItemsCollector collector = new StreamInfoItemsCollector(getServiceId()); + for (int i = 0; i < events.size(); i++) { - collector.commit(new MediaCCCRecentKioskExtractor(events.getObject(i))); + collector.commit(new MediaCCCRecentKioskExtractor(events.getObject(i), conferenceNames)); } return new InfoItemsPage<>(collector, null); } diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/extractors/MediaCCCRecentKioskExtractor.java b/extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/extractors/MediaCCCRecentKioskExtractor.java index 724b3e06b..ac75cfc72 100644 --- a/extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/extractors/MediaCCCRecentKioskExtractor.java +++ b/extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/extractors/MediaCCCRecentKioskExtractor.java @@ -1,22 +1,34 @@ package org.schabi.newpipe.extractor.services.media_ccc.extractors; import com.grack.nanojson.JsonObject; + +import org.schabi.newpipe.extractor.ServiceList; +import org.schabi.newpipe.extractor.channel.ChannelExtractor; +import org.schabi.newpipe.extractor.exceptions.ExtractionException; import org.schabi.newpipe.extractor.exceptions.ParsingException; import org.schabi.newpipe.extractor.localization.DateWrapper; import org.schabi.newpipe.extractor.services.media_ccc.linkHandler.MediaCCCConferenceLinkHandlerFactory; import org.schabi.newpipe.extractor.stream.StreamInfoItemExtractor; import org.schabi.newpipe.extractor.stream.StreamType; -import javax.annotation.Nullable; +import java.io.IOException; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; +import java.util.Map; + +import javax.annotation.Nullable; + +import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty; public class MediaCCCRecentKioskExtractor implements StreamInfoItemExtractor { private final JsonObject event; + private final Map conferenceNames; - public MediaCCCRecentKioskExtractor(final JsonObject event) { + public MediaCCCRecentKioskExtractor(final JsonObject event, + final Map conferenceNames) { this.event = event; + this.conferenceNames = conferenceNames; } @Override @@ -56,7 +68,25 @@ public class MediaCCCRecentKioskExtractor implements StreamInfoItemExtractor { @Override public String getUploaderName() throws ParsingException { - return ""; + final String conferenceApiUrl = event.getString("conference_url"); + if (isNullOrEmpty(conferenceApiUrl)) { + throw new ParsingException("conference url is empty"); + } + + if (conferenceNames.containsKey(conferenceApiUrl)) { + return conferenceNames.get(conferenceApiUrl); + } + + // get conference name from API. + try { + ChannelExtractor extractor = ServiceList.MediaCCC.getChannelExtractor( + new MediaCCCConferenceLinkHandlerFactory().fromUrl(conferenceApiUrl)); + extractor.fetchPage(); + conferenceNames.put(conferenceApiUrl, extractor.getName()); + return extractor.getName(); + } catch (IOException | ExtractionException e) { + throw new ParsingException("Could not get conference name from conference API URL", e); + } } @Override From 0cfefe222ac361c257bd88cdc4148aa200ff7e4e Mon Sep 17 00:00:00 2001 From: TobiGr Date: Sat, 26 Dec 2020 13:15:03 +0100 Subject: [PATCH 05/10] Revert "[media.ccc.de] Get conference name from API URL" This reverts commit d1a0686d2ffd3eedf0f503fc7c63ca8bd943d8e5. --- .../extractors/MediaCCCRecentKiosk.java | 10 ++---- .../MediaCCCRecentKioskExtractor.java | 36 ++----------------- 2 files changed, 5 insertions(+), 41 deletions(-) diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/extractors/MediaCCCRecentKiosk.java b/extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/extractors/MediaCCCRecentKiosk.java index 91da8976e..2656f7d64 100644 --- a/extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/extractors/MediaCCCRecentKiosk.java +++ b/extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/extractors/MediaCCCRecentKiosk.java @@ -4,7 +4,6 @@ import com.grack.nanojson.JsonArray; import com.grack.nanojson.JsonObject; import com.grack.nanojson.JsonParser; import com.grack.nanojson.JsonParserException; - import org.schabi.newpipe.extractor.Page; import org.schabi.newpipe.extractor.StreamingService; import org.schabi.newpipe.extractor.downloader.Downloader; @@ -15,16 +14,12 @@ import org.schabi.newpipe.extractor.linkhandler.ListLinkHandler; import org.schabi.newpipe.extractor.stream.StreamInfoItem; import org.schabi.newpipe.extractor.stream.StreamInfoItemsCollector; -import java.io.IOException; -import java.util.HashMap; -import java.util.Map; - import javax.annotation.Nonnull; +import java.io.IOException; public class MediaCCCRecentKiosk extends KioskExtractor { private JsonObject doc; - private final Map conferenceNames = new HashMap<>(); public MediaCCCRecentKiosk(StreamingService streamingService, ListLinkHandler linkHandler, String kioskId) { super(streamingService, linkHandler, kioskId); @@ -46,9 +41,8 @@ public class MediaCCCRecentKiosk extends KioskExtractor { public InfoItemsPage getInitialPage() throws IOException, ExtractionException { final JsonArray events = doc.getArray("events"); StreamInfoItemsCollector collector = new StreamInfoItemsCollector(getServiceId()); - for (int i = 0; i < events.size(); i++) { - collector.commit(new MediaCCCRecentKioskExtractor(events.getObject(i), conferenceNames)); + collector.commit(new MediaCCCRecentKioskExtractor(events.getObject(i))); } return new InfoItemsPage<>(collector, null); } diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/extractors/MediaCCCRecentKioskExtractor.java b/extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/extractors/MediaCCCRecentKioskExtractor.java index ac75cfc72..724b3e06b 100644 --- a/extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/extractors/MediaCCCRecentKioskExtractor.java +++ b/extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/extractors/MediaCCCRecentKioskExtractor.java @@ -1,34 +1,22 @@ package org.schabi.newpipe.extractor.services.media_ccc.extractors; import com.grack.nanojson.JsonObject; - -import org.schabi.newpipe.extractor.ServiceList; -import org.schabi.newpipe.extractor.channel.ChannelExtractor; -import org.schabi.newpipe.extractor.exceptions.ExtractionException; import org.schabi.newpipe.extractor.exceptions.ParsingException; import org.schabi.newpipe.extractor.localization.DateWrapper; import org.schabi.newpipe.extractor.services.media_ccc.linkHandler.MediaCCCConferenceLinkHandlerFactory; import org.schabi.newpipe.extractor.stream.StreamInfoItemExtractor; import org.schabi.newpipe.extractor.stream.StreamType; -import java.io.IOException; +import javax.annotation.Nullable; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; -import java.util.Map; - -import javax.annotation.Nullable; - -import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty; public class MediaCCCRecentKioskExtractor implements StreamInfoItemExtractor { private final JsonObject event; - private final Map conferenceNames; - public MediaCCCRecentKioskExtractor(final JsonObject event, - final Map conferenceNames) { + public MediaCCCRecentKioskExtractor(final JsonObject event) { this.event = event; - this.conferenceNames = conferenceNames; } @Override @@ -68,25 +56,7 @@ public class MediaCCCRecentKioskExtractor implements StreamInfoItemExtractor { @Override public String getUploaderName() throws ParsingException { - final String conferenceApiUrl = event.getString("conference_url"); - if (isNullOrEmpty(conferenceApiUrl)) { - throw new ParsingException("conference url is empty"); - } - - if (conferenceNames.containsKey(conferenceApiUrl)) { - return conferenceNames.get(conferenceApiUrl); - } - - // get conference name from API. - try { - ChannelExtractor extractor = ServiceList.MediaCCC.getChannelExtractor( - new MediaCCCConferenceLinkHandlerFactory().fromUrl(conferenceApiUrl)); - extractor.fetchPage(); - conferenceNames.put(conferenceApiUrl, extractor.getName()); - return extractor.getName(); - } catch (IOException | ExtractionException e) { - throw new ParsingException("Could not get conference name from conference API URL", e); - } + return ""; } @Override From 090dade5b338c278c9a6c8f51c25b6bd2d45d1ed Mon Sep 17 00:00:00 2001 From: TobiGr Date: Sun, 27 Dec 2020 01:03:08 +0100 Subject: [PATCH 06/10] [media.ccc.de] Add short test for recent kiosk --- .../MediaCCCRecentListExtractorTest.java | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 extractor/src/test/java/org/schabi/newpipe/extractor/services/media_ccc/MediaCCCRecentListExtractorTest.java diff --git a/extractor/src/test/java/org/schabi/newpipe/extractor/services/media_ccc/MediaCCCRecentListExtractorTest.java b/extractor/src/test/java/org/schabi/newpipe/extractor/services/media_ccc/MediaCCCRecentListExtractorTest.java new file mode 100644 index 000000000..26d403697 --- /dev/null +++ b/extractor/src/test/java/org/schabi/newpipe/extractor/services/media_ccc/MediaCCCRecentListExtractorTest.java @@ -0,0 +1,41 @@ +package org.schabi.newpipe.extractor.services.media_ccc; + +import org.junit.BeforeClass; +import org.junit.Test; +import org.schabi.newpipe.DownloaderTestImpl; +import org.schabi.newpipe.extractor.NewPipe; +import org.schabi.newpipe.extractor.kiosk.KioskExtractor; +import org.schabi.newpipe.extractor.stream.StreamInfoItem; + +import java.time.OffsetDateTime; +import java.util.List; + +import static org.junit.Assert.*; +import static org.schabi.newpipe.extractor.ServiceList.MediaCCC; +import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty; + +public class MediaCCCRecentListExtractorTest { + private static KioskExtractor extractor; + + @BeforeClass + public static void setUpClass() throws Exception { + NewPipe.init(DownloaderTestImpl.getInstance()); + extractor = MediaCCC.getKioskList().getExtractorById("recent", null); + extractor.fetchPage(); + } + + @Test + public void testStreamList() throws Exception { + final List items = extractor.getInitialPage().getItems(); + assertEquals(100, items.size()); + for (final StreamInfoItem item: items) { + assertFalse(isNullOrEmpty(item.getName())); + assertTrue(item.getDuration() > 0); + assertTrue(isNullOrEmpty(item.getUploaderName())); // we do not get the uploader name + assertTrue(item.getUploadDate().offsetDateTime().isBefore(OffsetDateTime.now())); + assertTrue(item.getUploadDate().offsetDateTime().isAfter(OffsetDateTime.now().minusYears(1))); + } + } + + +} From 674f2227f3a1ca11cbc9101a9d6211ad7babd42a Mon Sep 17 00:00:00 2001 From: TobiGr Date: Sun, 27 Dec 2020 01:27:37 +0100 Subject: [PATCH 07/10] make url pattern static --- .../org/schabi/newpipe/extractor/kiosk/KioskExtractor.java | 4 ++-- .../linkHandler/MediaCCCRecentListLinkHandlerFactory.java | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/kiosk/KioskExtractor.java b/extractor/src/main/java/org/schabi/newpipe/extractor/kiosk/KioskExtractor.java index 448905246..7abefbd94 100644 --- a/extractor/src/main/java/org/schabi/newpipe/extractor/kiosk/KioskExtractor.java +++ b/extractor/src/main/java/org/schabi/newpipe/extractor/kiosk/KioskExtractor.java @@ -45,11 +45,11 @@ public abstract class KioskExtractor extends ListExtractor Date: Sun, 27 Dec 2020 11:01:10 +0100 Subject: [PATCH 08/10] [media.ccc.de] Escape "." in id pattern --- .../linkHandler/MediaCCCRecentListLinkHandlerFactory.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/linkHandler/MediaCCCRecentListLinkHandlerFactory.java b/extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/linkHandler/MediaCCCRecentListLinkHandlerFactory.java index 2d744ca54..8b22577b8 100644 --- a/extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/linkHandler/MediaCCCRecentListLinkHandlerFactory.java +++ b/extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/linkHandler/MediaCCCRecentListLinkHandlerFactory.java @@ -6,7 +6,7 @@ import java.util.List; import java.util.regex.Pattern; public class MediaCCCRecentListLinkHandlerFactory extends ListLinkHandlerFactory { - private static final String pattern = "^(https?://)?media.ccc.de/recent/?$"; + private static final String pattern = "^(https?://)?media\\.ccc\\.de/recent/?$"; @Override public String getId(String url) { From 80f4d42226af1c704cd0a98a3538f3cca4654510 Mon Sep 17 00:00:00 2001 From: TobiGr Date: Sun, 27 Dec 2020 01:28:08 +0100 Subject: [PATCH 09/10] [Draft] Add live extractor --- .../services/media_ccc/MediaCCCService.java | 10 + .../MediaCCCLiveStreamExtractor.java | 86 + .../extractors/MediaCCCLiveStreamKiosk.java | 1976 +++++++++++++++++ ...iaCCCLiveStreamListLinkHandlerFactory.java | 27 + .../MediaCCCLiveStreamListExtractorTest.java | 39 + 5 files changed, 2138 insertions(+) create mode 100644 extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/extractors/MediaCCCLiveStreamExtractor.java create mode 100644 extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/extractors/MediaCCCLiveStreamKiosk.java create mode 100644 extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/linkHandler/MediaCCCLiveStreamListLinkHandlerFactory.java create mode 100644 extractor/src/test/java/org/schabi/newpipe/extractor/services/media_ccc/MediaCCCLiveStreamListExtractorTest.java diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/MediaCCCService.java b/extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/MediaCCCService.java index d3ed918a8..e7f41b313 100644 --- a/extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/MediaCCCService.java +++ b/extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/MediaCCCService.java @@ -102,6 +102,16 @@ public class MediaCCCService extends StreamingService { } }, new MediaCCCRecentListLinkHandlerFactory(), "recent"); + list.addKioskEntry(new KioskList.KioskExtractorFactory() { + @Override + public KioskExtractor createNewKiosk(final StreamingService streamingService, + final String url, final String kioskId) + throws ExtractionException { + return new MediaCCCLiveStreamKiosk(MediaCCCService.this, + new MediaCCCLiveStreamListLinkHandlerFactory().fromUrl(url), kioskId); + } + }, new MediaCCCLiveStreamListLinkHandlerFactory(), "live"); + list.setDefaultKiosk("recent"); } catch (Exception e) { throw new ExtractionException(e); diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/extractors/MediaCCCLiveStreamExtractor.java b/extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/extractors/MediaCCCLiveStreamExtractor.java new file mode 100644 index 000000000..c2f6e5ff0 --- /dev/null +++ b/extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/extractors/MediaCCCLiveStreamExtractor.java @@ -0,0 +1,86 @@ +package org.schabi.newpipe.extractor.services.media_ccc.extractors; + +import com.grack.nanojson.JsonObject; +import org.schabi.newpipe.extractor.exceptions.ParsingException; +import org.schabi.newpipe.extractor.localization.DateWrapper; +import org.schabi.newpipe.extractor.stream.StreamInfoItemExtractor; +import org.schabi.newpipe.extractor.stream.StreamType; + +import javax.annotation.Nullable; + +public class MediaCCCLiveStreamExtractor implements StreamInfoItemExtractor { + + private final JsonObject conferenceInfo; + private final String group; + private final JsonObject roomInfo; + + public MediaCCCLiveStreamExtractor(JsonObject conferenceInfo, String group, JsonObject roomInfo) { + this.conferenceInfo = conferenceInfo; + this.group = group; + this.roomInfo = roomInfo; + } + + @Override + public String getName() throws ParsingException { + return roomInfo.getString("schedulename"); + } + + @Override + public String getUrl() throws ParsingException { + return roomInfo.getString("link"); + } + + @Override + public String getThumbnailUrl() throws ParsingException { + return roomInfo.getString("thumb"); + } + + @Override + public StreamType getStreamType() throws ParsingException { + boolean isVideo = false; + for (Object stream : roomInfo.getArray("streams")) { + if ("video".equals(((JsonObject) stream).getString("type"))) { + isVideo = true; + break; + } + } + return isVideo ? StreamType.LIVE_STREAM : StreamType.AUDIO_LIVE_STREAM; + } + + @Override + public boolean isAd() throws ParsingException { + return false; + } + + @Override + public long getDuration() throws ParsingException { + return 0; + } + + @Override + public long getViewCount() throws ParsingException { + return -1; + } + + @Override + public String getUploaderName() throws ParsingException { + return conferenceInfo.getString("conference"); + } + + @Override + public String getUploaderUrl() throws ParsingException { + return "https://media.ccc.de/c/" + conferenceInfo.getString("slug"); + } + + @Nullable + @Override + public String getTextualUploadDate() throws ParsingException { + return null; + } + + @Nullable + @Override + public DateWrapper getUploadDate() throws ParsingException { + return null; + } +} diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/extractors/MediaCCCLiveStreamKiosk.java b/extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/extractors/MediaCCCLiveStreamKiosk.java new file mode 100644 index 000000000..5e6a9945f --- /dev/null +++ b/extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/extractors/MediaCCCLiveStreamKiosk.java @@ -0,0 +1,1976 @@ +package org.schabi.newpipe.extractor.services.media_ccc.extractors; + +import com.grack.nanojson.JsonArray; +import com.grack.nanojson.JsonObject; +import com.grack.nanojson.JsonParser; +import com.grack.nanojson.JsonParserException; +import org.schabi.newpipe.extractor.Page; +import org.schabi.newpipe.extractor.StreamingService; +import org.schabi.newpipe.extractor.downloader.Downloader; +import org.schabi.newpipe.extractor.exceptions.ExtractionException; +import org.schabi.newpipe.extractor.exceptions.ParsingException; +import org.schabi.newpipe.extractor.kiosk.KioskExtractor; +import org.schabi.newpipe.extractor.linkhandler.ListLinkHandler; +import org.schabi.newpipe.extractor.stream.StreamInfoItem; +import org.schabi.newpipe.extractor.stream.StreamInfoItemsCollector; + +import javax.annotation.Nonnull; +import java.io.IOException; + +public class MediaCCCLiveStreamKiosk extends KioskExtractor { + public JsonArray doc; + + public MediaCCCLiveStreamKiosk(StreamingService streamingService, ListLinkHandler linkHandler, String kioskId) { + super(streamingService, linkHandler, kioskId); + } + + @Override + public void onFetchPage(@Nonnull Downloader downloader) throws IOException, ExtractionException { + String site = downloader.get("https://streaming.media.ccc.de/streams/v2.json", + getExtractorLocalization()).responseBody(); + // for testing, from + /*site = "[\n" + + " {\n" + + " \"conference\": \"32C3\",\n" + + " \"slug\": \"32c3\",\n" + + " \"author\": \"CCC\",\n" + + " \"description\": \"Live-Streaming vom 32C3\",\n" + + " \"keywords\": \"32C3, Hacking, Chaos Computer Club, Video, Music, Podcast, Media, Streaming, Hacker, Hamburg\",\n" + + " \"startsAt\": \"2015-12-27T05:00:00+0000\",\n" + + " \"endsAt\": \"2015-12-30T20:00:00+0000\",\n" + + " \"groups\": [\n" + + " {\n" + + " \"group\": \"Lecture Rooms\",\n" + + " \"rooms\": [\n" + + " {\n" + + " \"slug\": \"hall1\",\n" + + " \"schedulename\": \"Hall 1\",\n" + + " \"thumb\": \"https://static.media.ccc.de/media/conferences/archconf/2020/6390-hd.jpg\",\n" + + " \"link\": \"http://localhost:8000/streams/32c3/hall1?forceopen=yess\",\n" + + " \"display\": \"Hall 1\",\n" + + " \"streams\": [\n" + + " {\n" + + " \"slug\": \"hd-native\",\n" + + " \"display\": \"Hall 1 FullHD Video\",\n" + + " \"type\": \"video\",\n" + + " \"isTranslated\": false,\n" + + " \"videoSize\": [\n" + + " 1920,\n" + + " 1080\n" + + " ],\n" + + " \"urls\": {\n" + + " \"webm\": {\n" + + " \"display\": \"WebM\",\n" + + " \"tech\": \"1920x1080, VP8+Vorbis in WebM, 2.8 MBit/s\",\n" + + " \"url\": \"http://cdn.c3voc.de/s1_native_hd.webm\"\n" + + " },\n" + + " \"hls\": {\n" + + " \"display\": \"HLS\",\n" + + " \"tech\": \"1920x1080, h264+AAC im MPEG-TS-Container via HTTP, 3 MBit/s\",\n" + + " \"url\": \"http://cdn.c3voc.de/hls/s1_native_hd.m3u8\"\n" + + " }\n" + + " }\n" + + " },\n" + + " {\n" + + " \"slug\": \"hd-translated\",\n" + + " \"display\": \"Hall 1 FullHD Video (Translation)\",\n" + + " \"type\": \"video\",\n" + + " \"isTranslated\": true,\n" + + " \"videoSize\": [\n" + + " 1920,\n" + + " 1080\n" + + " ],\n" + + " \"urls\": {\n" + + " \"webm\": {\n" + + " \"display\": \"WebM\",\n" + + " \"tech\": \"1920x1080, VP8+Vorbis in WebM, 2.8 MBit/s\",\n" + + " \"url\": \"http://cdn.c3voc.de/s1_translated_hd.webm\"\n" + + " },\n" + + " \"hls\": {\n" + + " \"display\": \"HLS\",\n" + + " \"tech\": \"1920x1080, h264+AAC im MPEG-TS-Container via HTTP, 3 MBit/s\",\n" + + " \"url\": \"http://cdn.c3voc.de/hls/s1_translated_hd.m3u8\"\n" + + " }\n" + + " }\n" + + " },\n" + + " {\n" + + " \"slug\": \"sd-native\",\n" + + " \"display\": \"Hall 1 SD Video\",\n" + + " \"type\": \"video\",\n" + + " \"isTranslated\": false,\n" + + " \"videoSize\": [\n" + + " 1024,\n" + + " 576\n" + + " ],\n" + + " \"urls\": {\n" + + " \"webm\": {\n" + + " \"display\": \"WebM\",\n" + + " \"tech\": \"1024x576, VP8+Vorbis in WebM, 800 kBit/s\",\n" + + " \"url\": \"http://cdn.c3voc.de/s1_native_sd.webm\"\n" + + " },\n" + + " \"hls\": {\n" + + " \"display\": \"HLS\",\n" + + " \"tech\": \"1024x576, h264+AAC im MPEG-TS-Container via HTTP, 800 kBit/s\",\n" + + " \"url\": \"http://cdn.c3voc.de/hls/s1_native_sd.m3u8\"\n" + + " }\n" + + " }\n" + + " },\n" + + " {\n" + + " \"slug\": \"sd-translated\",\n" + + " \"display\": \"Hall 1 SD Video (Translation)\",\n" + + " \"type\": \"video\",\n" + + " \"isTranslated\": true,\n" + + " \"videoSize\": [\n" + + " 1024,\n" + + " 576\n" + + " ],\n" + + " \"urls\": {\n" + + " \"webm\": {\n" + + " \"display\": \"WebM\",\n" + + " \"tech\": \"1024x576, VP8+Vorbis in WebM, 800 kBit/s\",\n" + + " \"url\": \"http://cdn.c3voc.de/s1_translated_sd.webm\"\n" + + " },\n" + + " \"hls\": {\n" + + " \"display\": \"HLS\",\n" + + " \"tech\": \"1024x576, h264+AAC im MPEG-TS-Container via HTTP, 800 kBit/s\",\n" + + " \"url\": \"http://cdn.c3voc.de/hls/s1_translated_sd.m3u8\"\n" + + " }\n" + + " }\n" + + " },\n" + + " {\n" + + " \"slug\": \"slides-native\",\n" + + " \"display\": \"Hall 1 Slides\",\n" + + " \"type\": \"slides\",\n" + + " \"isTranslated\": false,\n" + + " \"videoSize\": [\n" + + " 1024,\n" + + " 576\n" + + " ],\n" + + " \"urls\": {\n" + + " \"webm\": {\n" + + " \"display\": \"WebM\",\n" + + " \"tech\": \"1024x576, VP8+Vorbis in WebM, XXX kBit/s\",\n" + + " \"url\": \"http://cdn.c3voc.de/s1_native_slides.webm\"\n" + + " },\n" + + " \"hls\": {\n" + + " \"display\": \"HLS\",\n" + + " \"tech\": \"1024x576, h264+AAC im MPEG-TS-Container via HTTP, XXX kBit/s\",\n" + + " \"url\": \"http://cdn.c3voc.de/hls/s1_native_slides.m3u8\"\n" + + " }\n" + + " }\n" + + " },\n" + + " {\n" + + " \"slug\": \"slides-translated\",\n" + + " \"display\": \"Hall 1 Slides (Translation)\",\n" + + " \"type\": \"slides\",\n" + + " \"isTranslated\": true,\n" + + " \"videoSize\": [\n" + + " 1024,\n" + + " 576\n" + + " ],\n" + + " \"urls\": {\n" + + " \"webm\": {\n" + + " \"display\": \"WebM\",\n" + + " \"tech\": \"1024x576, VP8+Vorbis in WebM, XXX kBit/s\",\n" + + " \"url\": \"http://cdn.c3voc.de/s1_translated_slides.webm\"\n" + + " },\n" + + " \"hls\": {\n" + + " \"display\": \"HLS\",\n" + + " \"tech\": \"1024x576, h264+AAC im MPEG-TS-Container via HTTP, XXX kBit/s\",\n" + + " \"url\": \"http://cdn.c3voc.de/hls/s1_translated_slides.m3u8\"\n" + + " }\n" + + " }\n" + + " },\n" + + " {\n" + + " \"slug\": \"audio-native\",\n" + + " \"display\": \"Hall 1 Audio\",\n" + + " \"type\": \"audio\",\n" + + " \"isTranslated\": false,\n" + + " \"videoSize\": null,\n" + + " \"urls\": {\n" + + " \"mp3\": {\n" + + " \"display\": \"MP3\",\n" + + " \"tech\": \"MP3-Audio, 96 kBit/s\",\n" + + " \"url\": \"http://cdn.c3voc.de/s1_native.mp3\"\n" + + " },\n" + + " \"opus\": {\n" + + " \"display\": \"Opus\",\n" + + " \"tech\": \"Opus-Audio, 64 kBit/s\",\n" + + " \"url\": \"http://cdn.c3voc.de/s1_native.opus\"\n" + + " }\n" + + " }\n" + + " },\n" + + " {\n" + + " \"slug\": \"audio-translated\",\n" + + " \"display\": \"Hall 1 Audio (Translation)\",\n" + + " \"type\": \"audio\",\n" + + " \"isTranslated\": true,\n" + + " \"videoSize\": null,\n" + + " \"urls\": {\n" + + " \"mp3\": {\n" + + " \"display\": \"MP3\",\n" + + " \"tech\": \"MP3-Audio, 96 kBit/s\",\n" + + " \"url\": \"http://cdn.c3voc.de/s1_translated.mp3\"\n" + + " },\n" + + " \"opus\": {\n" + + " \"display\": \"Opus\",\n" + + " \"tech\": \"Opus-Audio, 64 kBit/s\",\n" + + " \"url\": \"http://cdn.c3voc.de/s1_translated.opus\"\n" + + " }\n" + + " }\n" + + " }\n" + + " ]\n" + + " },\n" + + " {\n" + + " \"slug\": \"hall2\",\n" + + " \"schedulename\": \"Hall 2\",\n" + + " \"thumb\": \"https://static.media.ccc.de/media/conferences/archconf/2020/6390-hd.jpg\",\n" + + " \"link\": \"http://localhost:8000/streams/32c3/hall2?forceopen=yess\",\n" + + " \"display\": \"Hall 2\",\n" + + " \"streams\": [\n" + + " {\n" + + " \"slug\": \"hd-native\",\n" + + " \"display\": \"Hall 2 FullHD Video\",\n" + + " \"type\": \"video\",\n" + + " \"isTranslated\": false,\n" + + " \"videoSize\": [\n" + + " 1920,\n" + + " 1080\n" + + " ],\n" + + " \"urls\": {\n" + + " \"webm\": {\n" + + " \"display\": \"WebM\",\n" + + " \"tech\": \"1920x1080, VP8+Vorbis in WebM, 2.8 MBit/s\",\n" + + " \"url\": \"http://cdn.c3voc.de/s2_native_hd.webm\"\n" + + " },\n" + + " \"hls\": {\n" + + " \"display\": \"HLS\",\n" + + " \"tech\": \"1920x1080, h264+AAC im MPEG-TS-Container via HTTP, 3 MBit/s\",\n" + + " \"url\": \"http://cdn.c3voc.de/hls/s2_native_hd.m3u8\"\n" + + " }\n" + + " }\n" + + " },\n" + + " {\n" + + " \"slug\": \"hd-translated\",\n" + + " \"display\": \"Hall 2 FullHD Video (Translation)\",\n" + + " \"type\": \"video\",\n" + + " \"isTranslated\": true,\n" + + " \"videoSize\": [\n" + + " 1920,\n" + + " 1080\n" + + " ],\n" + + " \"urls\": {\n" + + " \"webm\": {\n" + + " \"display\": \"WebM\",\n" + + " \"tech\": \"1920x1080, VP8+Vorbis in WebM, 2.8 MBit/s\",\n" + + " \"url\": \"http://cdn.c3voc.de/s2_translated_hd.webm\"\n" + + " },\n" + + " \"hls\": {\n" + + " \"display\": \"HLS\",\n" + + " \"tech\": \"1920x1080, h264+AAC im MPEG-TS-Container via HTTP, 3 MBit/s\",\n" + + " \"url\": \"http://cdn.c3voc.de/hls/s2_translated_hd.m3u8\"\n" + + " }\n" + + " }\n" + + " },\n" + + " {\n" + + " \"slug\": \"sd-native\",\n" + + " \"display\": \"Hall 2 SD Video\",\n" + + " \"type\": \"video\",\n" + + " \"isTranslated\": false,\n" + + " \"videoSize\": [\n" + + " 1024,\n" + + " 576\n" + + " ],\n" + + " \"urls\": {\n" + + " \"webm\": {\n" + + " \"display\": \"WebM\",\n" + + " \"tech\": \"1024x576, VP8+Vorbis in WebM, 800 kBit/s\",\n" + + " \"url\": \"http://cdn.c3voc.de/s2_native_sd.webm\"\n" + + " },\n" + + " \"hls\": {\n" + + " \"display\": \"HLS\",\n" + + " \"tech\": \"1024x576, h264+AAC im MPEG-TS-Container via HTTP, 800 kBit/s\",\n" + + " \"url\": \"http://cdn.c3voc.de/hls/s2_native_sd.m3u8\"\n" + + " }\n" + + " }\n" + + " },\n" + + " {\n" + + " \"slug\": \"sd-translated\",\n" + + " \"display\": \"Hall 2 SD Video (Translation)\",\n" + + " \"type\": \"video\",\n" + + " \"isTranslated\": true,\n" + + " \"videoSize\": [\n" + + " 1024,\n" + + " 576\n" + + " ],\n" + + " \"urls\": {\n" + + " \"webm\": {\n" + + " \"display\": \"WebM\",\n" + + " \"tech\": \"1024x576, VP8+Vorbis in WebM, 800 kBit/s\",\n" + + " \"url\": \"http://cdn.c3voc.de/s2_translated_sd.webm\"\n" + + " },\n" + + " \"hls\": {\n" + + " \"display\": \"HLS\",\n" + + " \"tech\": \"1024x576, h264+AAC im MPEG-TS-Container via HTTP, 800 kBit/s\",\n" + + " \"url\": \"http://cdn.c3voc.de/hls/s2_translated_sd.m3u8\"\n" + + " }\n" + + " }\n" + + " },\n" + + " {\n" + + " \"slug\": \"slides-native\",\n" + + " \"display\": \"Hall 2 Slides\",\n" + + " \"type\": \"slides\",\n" + + " \"isTranslated\": false,\n" + + " \"videoSize\": [\n" + + " 1024,\n" + + " 576\n" + + " ],\n" + + " \"urls\": {\n" + + " \"webm\": {\n" + + " \"display\": \"WebM\",\n" + + " \"tech\": \"1024x576, VP8+Vorbis in WebM, XXX kBit/s\",\n" + + " \"url\": \"http://cdn.c3voc.de/s2_native_slides.webm\"\n" + + " },\n" + + " \"hls\": {\n" + + " \"display\": \"HLS\",\n" + + " \"tech\": \"1024x576, h264+AAC im MPEG-TS-Container via HTTP, XXX kBit/s\",\n" + + " \"url\": \"http://cdn.c3voc.de/hls/s2_native_slides.m3u8\"\n" + + " }\n" + + " }\n" + + " },\n" + + " {\n" + + " \"slug\": \"slides-translated\",\n" + + " \"display\": \"Hall 2 Slides (Translation)\",\n" + + " \"type\": \"slides\",\n" + + " \"isTranslated\": true,\n" + + " \"videoSize\": [\n" + + " 1024,\n" + + " 576\n" + + " ],\n" + + " \"urls\": {\n" + + " \"webm\": {\n" + + " \"display\": \"WebM\",\n" + + " \"tech\": \"1024x576, VP8+Vorbis in WebM, XXX kBit/s\",\n" + + " \"url\": \"http://cdn.c3voc.de/s2_translated_slides.webm\"\n" + + " },\n" + + " \"hls\": {\n" + + " \"display\": \"HLS\",\n" + + " \"tech\": \"1024x576, h264+AAC im MPEG-TS-Container via HTTP, XXX kBit/s\",\n" + + " \"url\": \"http://cdn.c3voc.de/hls/s2_translated_slides.m3u8\"\n" + + " }\n" + + " }\n" + + " },\n" + + " {\n" + + " \"slug\": \"audio-native\",\n" + + " \"display\": \"Hall 2 Audio\",\n" + + " \"type\": \"audio\",\n" + + " \"isTranslated\": false,\n" + + " \"videoSize\": null,\n" + + " \"urls\": {\n" + + " \"mp3\": {\n" + + " \"display\": \"MP3\",\n" + + " \"tech\": \"MP3-Audio, 96 kBit/s\",\n" + + " \"url\": \"http://cdn.c3voc.de/s2_native.mp3\"\n" + + " },\n" + + " \"opus\": {\n" + + " \"display\": \"Opus\",\n" + + " \"tech\": \"Opus-Audio, 64 kBit/s\",\n" + + " \"url\": \"http://cdn.c3voc.de/s2_native.opus\"\n" + + " }\n" + + " }\n" + + " },\n" + + " {\n" + + " \"slug\": \"audio-translated\",\n" + + " \"display\": \"Hall 2 Audio (Translation)\",\n" + + " \"type\": \"audio\",\n" + + " \"isTranslated\": true,\n" + + " \"videoSize\": null,\n" + + " \"urls\": {\n" + + " \"mp3\": {\n" + + " \"display\": \"MP3\",\n" + + " \"tech\": \"MP3-Audio, 96 kBit/s\",\n" + + " \"url\": \"http://cdn.c3voc.de/s2_translated.mp3\"\n" + + " },\n" + + " \"opus\": {\n" + + " \"display\": \"Opus\",\n" + + " \"tech\": \"Opus-Audio, 64 kBit/s\",\n" + + " \"url\": \"http://cdn.c3voc.de/s2_translated.opus\"\n" + + " }\n" + + " }\n" + + " }\n" + + " ]\n" + + " },\n" + + " {\n" + + " \"slug\": \"hallg\",\n" + + " \"schedulename\": \"Hall G\",\n" + + " \"thumb\": \"https://static.media.ccc.de/media/events/privacyweek/2020/368-hd.jpg\",\n" + + " \"link\": \"http://localhost:8000/streams/32c3/hallg?forceopen=yess\",\n" + + " \"display\": \"Hall G\",\n" + + " \"streams\": [\n" + + " {\n" + + " \"slug\": \"hd-native\",\n" + + " \"display\": \"Hall G FullHD Video\",\n" + + " \"type\": \"video\",\n" + + " \"isTranslated\": false,\n" + + " \"videoSize\": [\n" + + " 1920,\n" + + " 1080\n" + + " ],\n" + + " \"urls\": {\n" + + " \"webm\": {\n" + + " \"display\": \"WebM\",\n" + + " \"tech\": \"1920x1080, VP8+Vorbis in WebM, 2.8 MBit/s\",\n" + + " \"url\": \"http://cdn.c3voc.de/s3_native_hd.webm\"\n" + + " },\n" + + " \"hls\": {\n" + + " \"display\": \"HLS\",\n" + + " \"tech\": \"1920x1080, h264+AAC im MPEG-TS-Container via HTTP, 3 MBit/s\",\n" + + " \"url\": \"http://cdn.c3voc.de/hls/s3_native_hd.m3u8\"\n" + + " }\n" + + " }\n" + + " },\n" + + " {\n" + + " \"slug\": \"hd-translated\",\n" + + " \"display\": \"Hall G FullHD Video (Translation)\",\n" + + " \"type\": \"video\",\n" + + " \"isTranslated\": true,\n" + + " \"videoSize\": [\n" + + " 1920,\n" + + " 1080\n" + + " ],\n" + + " \"urls\": {\n" + + " \"webm\": {\n" + + " \"display\": \"WebM\",\n" + + " \"tech\": \"1920x1080, VP8+Vorbis in WebM, 2.8 MBit/s\",\n" + + " \"url\": \"http://cdn.c3voc.de/s3_translated_hd.webm\"\n" + + " },\n" + + " \"hls\": {\n" + + " \"display\": \"HLS\",\n" + + " \"tech\": \"1920x1080, h264+AAC im MPEG-TS-Container via HTTP, 3 MBit/s\",\n" + + " \"url\": \"http://cdn.c3voc.de/hls/s3_translated_hd.m3u8\"\n" + + " }\n" + + " }\n" + + " },\n" + + " {\n" + + " \"slug\": \"sd-native\",\n" + + " \"display\": \"Hall G SD Video\",\n" + + " \"type\": \"video\",\n" + + " \"isTranslated\": false,\n" + + " \"videoSize\": [\n" + + " 1024,\n" + + " 576\n" + + " ],\n" + + " \"urls\": {\n" + + " \"webm\": {\n" + + " \"display\": \"WebM\",\n" + + " \"tech\": \"1024x576, VP8+Vorbis in WebM, 800 kBit/s\",\n" + + " \"url\": \"http://cdn.c3voc.de/s3_native_sd.webm\"\n" + + " },\n" + + " \"hls\": {\n" + + " \"display\": \"HLS\",\n" + + " \"tech\": \"1024x576, h264+AAC im MPEG-TS-Container via HTTP, 800 kBit/s\",\n" + + " \"url\": \"http://cdn.c3voc.de/hls/s3_native_sd.m3u8\"\n" + + " }\n" + + " }\n" + + " },\n" + + " {\n" + + " \"slug\": \"sd-translated\",\n" + + " \"display\": \"Hall G SD Video (Translation)\",\n" + + " \"type\": \"video\",\n" + + " \"isTranslated\": true,\n" + + " \"videoSize\": [\n" + + " 1024,\n" + + " 576\n" + + " ],\n" + + " \"urls\": {\n" + + " \"webm\": {\n" + + " \"display\": \"WebM\",\n" + + " \"tech\": \"1024x576, VP8+Vorbis in WebM, 800 kBit/s\",\n" + + " \"url\": \"http://cdn.c3voc.de/s3_translated_sd.webm\"\n" + + " },\n" + + " \"hls\": {\n" + + " \"display\": \"HLS\",\n" + + " \"tech\": \"1024x576, h264+AAC im MPEG-TS-Container via HTTP, 800 kBit/s\",\n" + + " \"url\": \"http://cdn.c3voc.de/hls/s3_translated_sd.m3u8\"\n" + + " }\n" + + " }\n" + + " },\n" + + " {\n" + + " \"slug\": \"slides-native\",\n" + + " \"display\": \"Hall G Slides\",\n" + + " \"type\": \"slides\",\n" + + " \"isTranslated\": false,\n" + + " \"videoSize\": [\n" + + " 1024,\n" + + " 576\n" + + " ],\n" + + " \"urls\": {\n" + + " \"webm\": {\n" + + " \"display\": \"WebM\",\n" + + " \"tech\": \"1024x576, VP8+Vorbis in WebM, XXX kBit/s\",\n" + + " \"url\": \"http://cdn.c3voc.de/s3_native_slides.webm\"\n" + + " },\n" + + " \"hls\": {\n" + + " \"display\": \"HLS\",\n" + + " \"tech\": \"1024x576, h264+AAC im MPEG-TS-Container via HTTP, XXX kBit/s\",\n" + + " \"url\": \"http://cdn.c3voc.de/hls/s3_native_slides.m3u8\"\n" + + " }\n" + + " }\n" + + " },\n" + + " {\n" + + " \"slug\": \"slides-translated\",\n" + + " \"display\": \"Hall G Slides (Translation)\",\n" + + " \"type\": \"slides\",\n" + + " \"isTranslated\": true,\n" + + " \"videoSize\": [\n" + + " 1024,\n" + + " 576\n" + + " ],\n" + + " \"urls\": {\n" + + " \"webm\": {\n" + + " \"display\": \"WebM\",\n" + + " \"tech\": \"1024x576, VP8+Vorbis in WebM, XXX kBit/s\",\n" + + " \"url\": \"http://cdn.c3voc.de/s3_translated_slides.webm\"\n" + + " },\n" + + " \"hls\": {\n" + + " \"display\": \"HLS\",\n" + + " \"tech\": \"1024x576, h264+AAC im MPEG-TS-Container via HTTP, XXX kBit/s\",\n" + + " \"url\": \"http://cdn.c3voc.de/hls/s3_translated_slides.m3u8\"\n" + + " }\n" + + " }\n" + + " },\n" + + " {\n" + + " \"slug\": \"audio-native\",\n" + + " \"display\": \"Hall G Audio\",\n" + + " \"type\": \"audio\",\n" + + " \"isTranslated\": false,\n" + + " \"videoSize\": null,\n" + + " \"urls\": {\n" + + " \"mp3\": {\n" + + " \"display\": \"MP3\",\n" + + " \"tech\": \"MP3-Audio, 96 kBit/s\",\n" + + " \"url\": \"http://cdn.c3voc.de/s3_native.mp3\"\n" + + " },\n" + + " \"opus\": {\n" + + " \"display\": \"Opus\",\n" + + " \"tech\": \"Opus-Audio, 64 kBit/s\",\n" + + " \"url\": \"http://cdn.c3voc.de/s3_native.opus\"\n" + + " }\n" + + " }\n" + + " },\n" + + " {\n" + + " \"slug\": \"audio-translated\",\n" + + " \"display\": \"Hall G Audio (Translation)\",\n" + + " \"type\": \"audio\",\n" + + " \"isTranslated\": true,\n" + + " \"videoSize\": null,\n" + + " \"urls\": {\n" + + " \"mp3\": {\n" + + " \"display\": \"MP3\",\n" + + " \"tech\": \"MP3-Audio, 96 kBit/s\",\n" + + " \"url\": \"http://cdn.c3voc.de/s3_translated.mp3\"\n" + + " },\n" + + " \"opus\": {\n" + + " \"display\": \"Opus\",\n" + + " \"tech\": \"Opus-Audio, 64 kBit/s\",\n" + + " \"url\": \"http://cdn.c3voc.de/s3_translated.opus\"\n" + + " }\n" + + " }\n" + + " }\n" + + " ]\n" + + " },\n" + + " {\n" + + " \"slug\": \"hall6\",\n" + + " \"schedulename\": \"Hall 6\",\n" + + " \"thumb\": \"https://static.media.ccc.de/media/conferences/archconf/2020/6307-hd.jpg\",\n" + + " \"link\": \"http://localhost:8000/streams/32c3/hall6?forceopen=yess\",\n" + + " \"display\": \"Hall 6\",\n" + + " \"streams\": [\n" + + " {\n" + + " \"slug\": \"hd-native\",\n" + + " \"display\": \"Hall 6 FullHD Video\",\n" + + " \"type\": \"video\",\n" + + " \"isTranslated\": false,\n" + + " \"videoSize\": [\n" + + " 1920,\n" + + " 1080\n" + + " ],\n" + + " \"urls\": {\n" + + " \"webm\": {\n" + + " \"display\": \"WebM\",\n" + + " \"tech\": \"1920x1080, VP8+Vorbis in WebM, 2.8 MBit/s\",\n" + + " \"url\": \"http://cdn.c3voc.de/s4_native_hd.webm\"\n" + + " },\n" + + " \"hls\": {\n" + + " \"display\": \"HLS\",\n" + + " \"tech\": \"1920x1080, h264+AAC im MPEG-TS-Container via HTTP, 3 MBit/s\",\n" + + " \"url\": \"http://cdn.c3voc.de/hls/s4_native_hd.m3u8\"\n" + + " }\n" + + " }\n" + + " },\n" + + " {\n" + + " \"slug\": \"hd-translated\",\n" + + " \"display\": \"Hall 6 FullHD Video (Translation)\",\n" + + " \"type\": \"video\",\n" + + " \"isTranslated\": true,\n" + + " \"videoSize\": [\n" + + " 1920,\n" + + " 1080\n" + + " ],\n" + + " \"urls\": {\n" + + " \"webm\": {\n" + + " \"display\": \"WebM\",\n" + + " \"tech\": \"1920x1080, VP8+Vorbis in WebM, 2.8 MBit/s\",\n" + + " \"url\": \"http://cdn.c3voc.de/s4_translated_hd.webm\"\n" + + " },\n" + + " \"hls\": {\n" + + " \"display\": \"HLS\",\n" + + " \"tech\": \"1920x1080, h264+AAC im MPEG-TS-Container via HTTP, 3 MBit/s\",\n" + + " \"url\": \"http://cdn.c3voc.de/hls/s4_translated_hd.m3u8\"\n" + + " }\n" + + " }\n" + + " },\n" + + " {\n" + + " \"slug\": \"sd-native\",\n" + + " \"display\": \"Hall 6 SD Video\",\n" + + " \"type\": \"video\",\n" + + " \"isTranslated\": false,\n" + + " \"videoSize\": [\n" + + " 1024,\n" + + " 576\n" + + " ],\n" + + " \"urls\": {\n" + + " \"webm\": {\n" + + " \"display\": \"WebM\",\n" + + " \"tech\": \"1024x576, VP8+Vorbis in WebM, 800 kBit/s\",\n" + + " \"url\": \"http://cdn.c3voc.de/s4_native_sd.webm\"\n" + + " },\n" + + " \"hls\": {\n" + + " \"display\": \"HLS\",\n" + + " \"tech\": \"1024x576, h264+AAC im MPEG-TS-Container via HTTP, 800 kBit/s\",\n" + + " \"url\": \"http://cdn.c3voc.de/hls/s4_native_sd.m3u8\"\n" + + " }\n" + + " }\n" + + " },\n" + + " {\n" + + " \"slug\": \"sd-translated\",\n" + + " \"display\": \"Hall 6 SD Video (Translation)\",\n" + + " \"type\": \"video\",\n" + + " \"isTranslated\": true,\n" + + " \"videoSize\": [\n" + + " 1024,\n" + + " 576\n" + + " ],\n" + + " \"urls\": {\n" + + " \"webm\": {\n" + + " \"display\": \"WebM\",\n" + + " \"tech\": \"1024x576, VP8+Vorbis in WebM, 800 kBit/s\",\n" + + " \"url\": \"http://cdn.c3voc.de/s4_translated_sd.webm\"\n" + + " },\n" + + " \"hls\": {\n" + + " \"display\": \"HLS\",\n" + + " \"tech\": \"1024x576, h264+AAC im MPEG-TS-Container via HTTP, 800 kBit/s\",\n" + + " \"url\": \"http://cdn.c3voc.de/hls/s4_translated_sd.m3u8\"\n" + + " }\n" + + " }\n" + + " },\n" + + " {\n" + + " \"slug\": \"slides-native\",\n" + + " \"display\": \"Hall 6 Slides\",\n" + + " \"type\": \"slides\",\n" + + " \"isTranslated\": false,\n" + + " \"videoSize\": [\n" + + " 1024,\n" + + " 576\n" + + " ],\n" + + " \"urls\": {\n" + + " \"webm\": {\n" + + " \"display\": \"WebM\",\n" + + " \"tech\": \"1024x576, VP8+Vorbis in WebM, XXX kBit/s\",\n" + + " \"url\": \"http://cdn.c3voc.de/s4_native_slides.webm\"\n" + + " },\n" + + " \"hls\": {\n" + + " \"display\": \"HLS\",\n" + + " \"tech\": \"1024x576, h264+AAC im MPEG-TS-Container via HTTP, XXX kBit/s\",\n" + + " \"url\": \"http://cdn.c3voc.de/hls/s4_native_slides.m3u8\"\n" + + " }\n" + + " }\n" + + " },\n" + + " {\n" + + " \"slug\": \"slides-translated\",\n" + + " \"display\": \"Hall 6 Slides (Translation)\",\n" + + " \"type\": \"slides\",\n" + + " \"isTranslated\": true,\n" + + " \"videoSize\": [\n" + + " 1024,\n" + + " 576\n" + + " ],\n" + + " \"urls\": {\n" + + " \"webm\": {\n" + + " \"display\": \"WebM\",\n" + + " \"tech\": \"1024x576, VP8+Vorbis in WebM, XXX kBit/s\",\n" + + " \"url\": \"http://cdn.c3voc.de/s4_translated_slides.webm\"\n" + + " },\n" + + " \"hls\": {\n" + + " \"display\": \"HLS\",\n" + + " \"tech\": \"1024x576, h264+AAC im MPEG-TS-Container via HTTP, XXX kBit/s\",\n" + + " \"url\": \"http://cdn.c3voc.de/hls/s4_translated_slides.m3u8\"\n" + + " }\n" + + " }\n" + + " },\n" + + " {\n" + + " \"slug\": \"audio-native\",\n" + + " \"display\": \"Hall 6 Audio\",\n" + + " \"type\": \"audio\",\n" + + " \"isTranslated\": false,\n" + + " \"videoSize\": null,\n" + + " \"urls\": {\n" + + " \"mp3\": {\n" + + " \"display\": \"MP3\",\n" + + " \"tech\": \"MP3-Audio, 96 kBit/s\",\n" + + " \"url\": \"http://cdn.c3voc.de/s4_native.mp3\"\n" + + " },\n" + + " \"opus\": {\n" + + " \"display\": \"Opus\",\n" + + " \"tech\": \"Opus-Audio, 64 kBit/s\",\n" + + " \"url\": \"http://cdn.c3voc.de/s4_native.opus\"\n" + + " }\n" + + " }\n" + + " },\n" + + " {\n" + + " \"slug\": \"audio-translated\",\n" + + " \"display\": \"Hall 6 Audio (Translation)\",\n" + + " \"type\": \"audio\",\n" + + " \"isTranslated\": true,\n" + + " \"videoSize\": null,\n" + + " \"urls\": {\n" + + " \"mp3\": {\n" + + " \"display\": \"MP3\",\n" + + " \"tech\": \"MP3-Audio, 96 kBit/s\",\n" + + " \"url\": \"http://cdn.c3voc.de/s4_translated.mp3\"\n" + + " },\n" + + " \"opus\": {\n" + + " \"display\": \"Opus\",\n" + + " \"tech\": \"Opus-Audio, 64 kBit/s\",\n" + + " \"url\": \"http://cdn.c3voc.de/s4_translated.opus\"\n" + + " }\n" + + " }\n" + + " }\n" + + " ]\n" + + " }\n" + + " ]\n" + + " },\n" + + " {\n" + + " \"group\": \"Live Music\",\n" + + " \"rooms\": [\n" + + " {\n" + + " \"slug\": \"lounge\",\n" + + " \"schedulename\": \"Lounge\",\n" + + " \"thumb\": \"https://static.media.ccc.de/media/events/BigBrotherAwards/2020/2-hd.jpg\",\n" + + " \"link\": \"http://localhost:8000/streams/32c3/lounge?forceopen=yess\",\n" + + " \"display\": \"Lounge\",\n" + + " \"streams\": [\n" + + " {\n" + + " \"slug\": \"music-native\",\n" + + " \"display\": \"Lounge Radio\",\n" + + " \"type\": \"music\",\n" + + " \"isTranslated\": false,\n" + + " \"videoSize\": null,\n" + + " \"urls\": {\n" + + " \"mp3\": {\n" + + " \"display\": \"MP3\",\n" + + " \"tech\": \"MP3-Audio, 192 kBit/s\",\n" + + " \"url\": \"http://cdn.c3voc.de/lounge.mp3\"\n" + + " },\n" + + " \"opus\": {\n" + + " \"display\": \"Opus\",\n" + + " \"tech\": \"Opus-Audio, 96 kBit/s\",\n" + + " \"url\": \"http://cdn.c3voc.de/lounge.opus\"\n" + + " }\n" + + " }\n" + + " }\n" + + " ]\n" + + " },\n" + + " {\n" + + " \"slug\": \"ambient\",\n" + + " \"schedulename\": \"Ambient\",\n" + + " \"thumb\": \"https://static.media.ccc.de/media/congress/2019/11175-hd.jpg\",\n" + + " \"link\": \"http://localhost:8000/streams/32c3/ambient?forceopen=yess\",\n" + + " \"display\": \"Ambient\",\n" + + " \"streams\": [\n" + + " {\n" + + " \"slug\": \"music-native\",\n" + + " \"display\": \"Ambient Radio\",\n" + + " \"type\": \"music\",\n" + + " \"isTranslated\": false,\n" + + " \"videoSize\": null,\n" + + " \"urls\": {\n" + + " \"mp3\": {\n" + + " \"display\": \"MP3\",\n" + + " \"tech\": \"MP3-Audio, 192 kBit/s\",\n" + + " \"url\": \"http://cdn.c3voc.de/ambient.mp3\"\n" + + " },\n" + + " \"opus\": {\n" + + " \"display\": \"Opus\",\n" + + " \"tech\": \"Opus-Audio, 96 kBit/s\",\n" + + " \"url\": \"http://cdn.c3voc.de/ambient.opus\"\n" + + " }\n" + + " }\n" + + " }\n" + + " ]\n" + + " },\n" + + " {\n" + + " \"slug\": \"dome\",\n" + + " \"schedulename\": \"Dome\",\n" + + " \"thumb\": \"https://static.media.ccc.de/media/regional/c4/openchaos/2011-hd.jpg\",\n" + + " \"link\": \"http://localhost:8000/streams/32c3/dome?forceopen=yess\",\n" + + " \"display\": \"Dome\",\n" + + " \"streams\": [\n" + + " {\n" + + " \"slug\": \"music-native\",\n" + + " \"display\": \"Dome Radio\",\n" + + " \"type\": \"music\",\n" + + " \"isTranslated\": false,\n" + + " \"videoSize\": null,\n" + + " \"urls\": {\n" + + " \"mp3\": {\n" + + " \"display\": \"MP3\",\n" + + " \"tech\": \"MP3-Audio, 192 kBit/s\",\n" + + " \"url\": \"http://cdn.c3voc.de/dome.mp3\"\n" + + " },\n" + + " \"opus\": {\n" + + " \"display\": \"Opus\",\n" + + " \"tech\": \"Opus-Audio, 96 kBit/s\",\n" + + " \"url\": \"http://cdn.c3voc.de/dome.opus\"\n" + + " }\n" + + " }\n" + + " }\n" + + " ]\n" + + " },\n" + + " {\n" + + " \"slug\": \"chaos-west\",\n" + + " \"schedulename\": \"Chaos-West\",\n" + + " \"thumb\": \"http://localhost:8000/streams/thumbs/chaos-west.png\",\n" + + " \"link\": \"http://localhost:8000/streams/32c3/chaos-west?forceopen=yess\",\n" + + " \"display\": \"Chaos-West\",\n" + + " \"streams\": [\n" + + " {\n" + + " \"slug\": \"music-native\",\n" + + " \"display\": \"Chaos-West Radio\",\n" + + " \"type\": \"music\",\n" + + " \"isTranslated\": false,\n" + + " \"videoSize\": null,\n" + + " \"urls\": {\n" + + " \"mp3\": {\n" + + " \"display\": \"MP3\",\n" + + " \"tech\": \"MP3-Audio, 192 kBit/s\",\n" + + " \"url\": \"http://cdn.c3voc.de/chaos-west.mp3\"\n" + + " },\n" + + " \"opus\": {\n" + + " \"display\": \"Opus\",\n" + + " \"tech\": \"Opus-Audio, 96 kBit/s\",\n" + + " \"url\": \"http://cdn.c3voc.de/chaos-west.opus\"\n" + + " }\n" + + " }\n" + + " }\n" + + " ]\n" + + " },\n" + + " {\n" + + " \"slug\": \"radio\",\n" + + " \"schedulename\": \"Radio Fairydust\",\n" + + " \"thumb\": \"http://localhost:8000/streams/thumbs/radio.png\",\n" + + " \"link\": \"http://localhost:8000/streams/32c3/radio?forceopen=yess\",\n" + + " \"display\": \"Radio Fairydust\",\n" + + " \"streams\": [\n" + + " {\n" + + " \"slug\": \"music-native\",\n" + + " \"display\": \"Radio Fairydust Radio\",\n" + + " \"type\": \"music\",\n" + + " \"isTranslated\": false,\n" + + " \"videoSize\": null,\n" + + " \"urls\": {\n" + + " \"mp3\": {\n" + + " \"display\": \"MP3\",\n" + + " \"tech\": \"MP3-Audio, 192 kBit/s\",\n" + + " \"url\": \"http://cdn.c3voc.de/radio.mp3\"\n" + + " },\n" + + " \"opus\": {\n" + + " \"display\": \"Opus\",\n" + + " \"tech\": \"Opus-Audio, 96 kBit/s\",\n" + + " \"url\": \"http://cdn.c3voc.de/radio.opus\"\n" + + " }\n" + + " }\n" + + " }\n" + + " ]\n" + + " }\n" + + " ]\n" + + " },\n" + + " {\n" + + " \"group\": \"Live Podcasts\",\n" + + " \"rooms\": [\n" + + " {\n" + + " \"slug\": \"sendezentrum\",\n" + + " \"schedulename\": \"Bühne\",\n" + + " \"thumb\": \"http://localhost:8000/streams/thumbs/s5.png\",\n" + + " \"link\": \"http://localhost:8000/streams/32c3/sendezentrum?forceopen=yess\",\n" + + " \"display\": \"Sendezentrum\",\n" + + " \"streams\": [\n" + + " {\n" + + " \"slug\": \"hd-native\",\n" + + " \"display\": \"Sendezentrum FullHD Video\",\n" + + " \"type\": \"video\",\n" + + " \"isTranslated\": false,\n" + + " \"videoSize\": [\n" + + " 1920,\n" + + " 1080\n" + + " ],\n" + + " \"urls\": {\n" + + " \"webm\": {\n" + + " \"display\": \"WebM\",\n" + + " \"tech\": \"1920x1080, VP8+Vorbis in WebM, 2.8 MBit/s\",\n" + + " \"url\": \"http://cdn.c3voc.de/s5_native_hd.webm\"\n" + + " },\n" + + " \"hls\": {\n" + + " \"display\": \"HLS\",\n" + + " \"tech\": \"1920x1080, h264+AAC im MPEG-TS-Container via HTTP, 3 MBit/s\",\n" + + " \"url\": \"http://cdn.c3voc.de/hls/s5_native_hd.m3u8\"\n" + + " }\n" + + " }\n" + + " },\n" + + " {\n" + + " \"slug\": \"sd-native\",\n" + + " \"display\": \"Sendezentrum SD Video\",\n" + + " \"type\": \"video\",\n" + + " \"isTranslated\": false,\n" + + " \"videoSize\": [\n" + + " 1024,\n" + + " 576\n" + + " ],\n" + + " \"urls\": {\n" + + " \"webm\": {\n" + + " \"display\": \"WebM\",\n" + + " \"tech\": \"1024x576, VP8+Vorbis in WebM, 800 kBit/s\",\n" + + " \"url\": \"http://cdn.c3voc.de/s5_native_sd.webm\"\n" + + " },\n" + + " \"hls\": {\n" + + " \"display\": \"HLS\",\n" + + " \"tech\": \"1024x576, h264+AAC im MPEG-TS-Container via HTTP, 800 kBit/s\",\n" + + " \"url\": \"http://cdn.c3voc.de/hls/s5_native_sd.m3u8\"\n" + + " }\n" + + " }\n" + + " },\n" + + " {\n" + + " \"slug\": \"audio-native\",\n" + + " \"display\": \"Sendezentrum Audio\",\n" + + " \"type\": \"audio\",\n" + + " \"isTranslated\": false,\n" + + " \"videoSize\": null,\n" + + " \"urls\": {\n" + + " \"mp3\": {\n" + + " \"display\": \"MP3\",\n" + + " \"tech\": \"MP3-Audio, 96 kBit/s\",\n" + + " \"url\": \"http://cdn.c3voc.de/s5_native.mp3\"\n" + + " },\n" + + " \"opus\": {\n" + + " \"display\": \"Opus\",\n" + + " \"tech\": \"Opus-Audio, 64 kBit/s\",\n" + + " \"url\": \"http://cdn.c3voc.de/s5_native.opus\"\n" + + " }\n" + + " }\n" + + " }\n" + + " ]\n" + + " },\n" + + " {\n" + + " \"slug\": \"podcastertisch\",\n" + + " \"schedulename\": \"Podcaster-Tisch\",\n" + + " \"thumb\": \"http://localhost:8000/streams/thumbs/podcastertisch.png\",\n" + + " \"link\": \"http://localhost:8000/streams/32c3/podcastertisch?forceopen=yess\",\n" + + " \"display\": \"Sendezentrum - Podcastertisch\",\n" + + " \"streams\": [\n" + + " {\n" + + " \"slug\": \"music-native\",\n" + + " \"display\": \"Sendezentrum - Podcastertisch Radio\",\n" + + " \"type\": \"music\",\n" + + " \"isTranslated\": false,\n" + + " \"videoSize\": null,\n" + + " \"urls\": {\n" + + " \"mp3\": {\n" + + " \"display\": \"MP3\",\n" + + " \"tech\": \"MP3-Audio, 192 kBit/s\",\n" + + " \"url\": \"http://cdn.c3voc.de/podcastertisch.mp3\"\n" + + " },\n" + + " \"opus\": {\n" + + " \"display\": \"Opus\",\n" + + " \"tech\": \"Opus-Audio, 96 kBit/s\",\n" + + " \"url\": \"http://cdn.c3voc.de/podcastertisch.opus\"\n" + + " }\n" + + " }\n" + + " }\n" + + " ]\n" + + " }\n" + + " ]\n" + + " }\n" + + " ]\n" + + " }\n" + + "]";*/ + /*site = "[\n" + + " {\n" + + " \"author\": \"CCC\",\n" + + " \"conference\": \"33C3\",\n" + + " \"description\": \"Live-Streaming vom 33C3\",\n" + + " \"endsAt\": \"2016-12-30T20:00:00+0000\",\n" + + " \"groups\": [\n" + + " {\n" + + " \"group\": \"Live\",\n" + + " \"rooms\": [\n" + + " {\n" + + " \"display\": \"Saal 1\",\n" + + " \"link\": \"https://streaming.media.ccc.de/33c3/hall1\",\n" + + " \"schedulename\": \"Saal 1\",\n" + + " \"slug\": \"hall1\",\n" + + " \"streams\": [\n" + + " {\n" + + " \"display\": \"Saal 1 FullHD Video\",\n" + + " \"isTranslated\": false,\n" + + " \"slug\": \"hd-native\",\n" + + " \"type\": \"video\",\n" + + " \"urls\": {\n" + + " \"hls\": {\n" + + " \"display\": \"HLS\",\n" + + " \"tech\": \"1920x1080, h264+AAC im MPEG-TS-Container via HTTP, 3 MBit/s\",\n" + + " \"url\": \"https://cdn.c3voc.de/hls/s1_native_hd.m3u8\"\n" + + " },\n" + + " \"webm\": {\n" + + " \"display\": \"WebM\",\n" + + " \"tech\": \"1920x1080, VP8+Vorbis in WebM, 2.8 MBit/s\",\n" + + " \"url\": \"https://cdn.c3voc.de/s1_native_hd.webm\"\n" + + " }\n" + + " },\n" + + " \"videoSize\": [\n" + + " 1920,\n" + + " 1080\n" + + " ]\n" + + " },\n" + + " {\n" + + " \"display\": \"Saal 1 FullHD Video (Translation)\",\n" + + " \"isTranslated\": true,\n" + + " \"slug\": \"hd-translated\",\n" + + " \"type\": \"video\",\n" + + " \"urls\": {\n" + + " \"hls\": {\n" + + " \"display\": \"HLS\",\n" + + " \"tech\": \"1920x1080, h264+AAC im MPEG-TS-Container via HTTP, 3 MBit/s\",\n" + + " \"url\": \"https://cdn.c3voc.de/hls/s1_translated_hd.m3u8\"\n" + + " },\n" + + " \"webm\": {\n" + + " \"display\": \"WebM\",\n" + + " \"tech\": \"1920x1080, VP8+Vorbis in WebM, 2.8 MBit/s\",\n" + + " \"url\": \"https://cdn.c3voc.de/s1_translated_hd.webm\"\n" + + " }\n" + + " },\n" + + " \"videoSize\": [\n" + + " 1920,\n" + + " 1080\n" + + " ]\n" + + " },\n" + + " {\n" + + " \"display\": \"Saal 1 SD Video\",\n" + + " \"isTranslated\": false,\n" + + " \"slug\": \"sd-native\",\n" + + " \"type\": \"video\",\n" + + " \"urls\": {\n" + + " \"hls\": {\n" + + " \"display\": \"HLS\",\n" + + " \"tech\": \"1024x576, h264+AAC im MPEG-TS-Container via HTTP, 800 kBit/s\",\n" + + " \"url\": \"https://cdn.c3voc.de/hls/s1_native_sd.m3u8\"\n" + + " },\n" + + " \"webm\": {\n" + + " \"display\": \"WebM\",\n" + + " \"tech\": \"1024x576, VP8+Vorbis in WebM, 800 kBit/s\",\n" + + " \"url\": \"https://cdn.c3voc.de/s1_native_sd.webm\"\n" + + " }\n" + + " },\n" + + " \"videoSize\": [\n" + + " 1024,\n" + + " 576\n" + + " ]\n" + + " },\n" + + " {\n" + + " \"display\": \"Saal 1 SD Video (Translation)\",\n" + + " \"isTranslated\": true,\n" + + " \"slug\": \"sd-translated\",\n" + + " \"type\": \"video\",\n" + + " \"urls\": {\n" + + " \"hls\": {\n" + + " \"display\": \"HLS\",\n" + + " \"tech\": \"1024x576, h264+AAC im MPEG-TS-Container via HTTP, 800 kBit/s\",\n" + + " \"url\": \"https://cdn.c3voc.de/hls/s1_translated_sd.m3u8\"\n" + + " },\n" + + " \"webm\": {\n" + + " \"display\": \"WebM\",\n" + + " \"tech\": \"1024x576, VP8+Vorbis in WebM, 800 kBit/s\",\n" + + " \"url\": \"https://cdn.c3voc.de/s1_translated_sd.webm\"\n" + + " }\n" + + " },\n" + + " \"videoSize\": [\n" + + " 1024,\n" + + " 576\n" + + " ]\n" + + " },\n" + + " {\n" + + " \"display\": \"Saal 1 Slides\",\n" + + " \"isTranslated\": false,\n" + + " \"slug\": \"slides-native\",\n" + + " \"type\": \"slides\",\n" + + " \"urls\": {\n" + + " \"hls\": {\n" + + " \"display\": \"HLS\",\n" + + " \"tech\": \"1024x576, h264+AAC im MPEG-TS-Container via HTTP, XXX kBit/s\",\n" + + " \"url\": \"https://cdn.c3voc.de/hls/s1_native_slides.m3u8\"\n" + + " },\n" + + " \"webm\": {\n" + + " \"display\": \"WebM\",\n" + + " \"tech\": \"1024x576, VP8+Vorbis in WebM, XXX kBit/s\",\n" + + " \"url\": \"https://cdn.c3voc.de/s1_native_slides.webm\"\n" + + " }\n" + + " },\n" + + " \"videoSize\": [\n" + + " 1024,\n" + + " 576\n" + + " ]\n" + + " },\n" + + " {\n" + + " \"display\": \"Saal 1 Slides (Translation)\",\n" + + " \"isTranslated\": true,\n" + + " \"slug\": \"slides-translated\",\n" + + " \"type\": \"slides\",\n" + + " \"urls\": {\n" + + " \"hls\": {\n" + + " \"display\": \"HLS\",\n" + + " \"tech\": \"1024x576, h264+AAC im MPEG-TS-Container via HTTP, XXX kBit/s\",\n" + + " \"url\": \"https://cdn.c3voc.de/hls/s1_translated_slides.m3u8\"\n" + + " },\n" + + " \"webm\": {\n" + + " \"display\": \"WebM\",\n" + + " \"tech\": \"1024x576, VP8+Vorbis in WebM, XXX kBit/s\",\n" + + " \"url\": \"https://cdn.c3voc.de/s1_translated_slides.webm\"\n" + + " }\n" + + " },\n" + + " \"videoSize\": [\n" + + " 1024,\n" + + " 576\n" + + " ]\n" + + " },\n" + + " {\n" + + " \"display\": \"Saal 1 Audio\",\n" + + " \"isTranslated\": false,\n" + + " \"slug\": \"audio-native\",\n" + + " \"type\": \"audio\",\n" + + " \"urls\": {\n" + + " \"mp3\": {\n" + + " \"display\": \"MP3\",\n" + + " \"tech\": \"MP3-Audio, 96 kBit/s\",\n" + + " \"url\": \"https://cdn.c3voc.de/s1_native.mp3\"\n" + + " },\n" + + " \"opus\": {\n" + + " \"display\": \"Opus\",\n" + + " \"tech\": \"Opus-Audio, 64 kBit/s\",\n" + + " \"url\": \"https://cdn.c3voc.de/s1_native.opus\"\n" + + " }\n" + + " },\n" + + " \"videoSize\": null\n" + + " },\n" + + " {\n" + + " \"display\": \"Saal 1 Audio (Translation)\",\n" + + " \"isTranslated\": true,\n" + + " \"slug\": \"audio-translated\",\n" + + " \"type\": \"audio\",\n" + + " \"urls\": {\n" + + " \"mp3\": {\n" + + " \"display\": \"MP3\",\n" + + " \"tech\": \"MP3-Audio, 96 kBit/s\",\n" + + " \"url\": \"https://cdn.c3voc.de/s1_translated.mp3\"\n" + + " },\n" + + " \"opus\": {\n" + + " \"display\": \"Opus\",\n" + + " \"tech\": \"Opus-Audio, 64 kBit/s\",\n" + + " \"url\": \"https://cdn.c3voc.de/s1_translated.opus\"\n" + + " }\n" + + " },\n" + + " \"videoSize\": null\n" + + " }\n" + + " ],\n" + + " \"thumb\": \"https://streaming.media.ccc.de/thumbs/s1.png\"\n" + + " },\n" + + " {\n" + + " \"display\": \"Saal 2\",\n" + + " \"link\": \"https://streaming.media.ccc.de/33c3/hall2\",\n" + + " \"schedulename\": \"Saal 2\",\n" + + " \"slug\": \"hall2\",\n" + + " \"streams\": [\n" + + " {\n" + + " \"display\": \"Saal 2 FullHD Video\",\n" + + " \"isTranslated\": false,\n" + + " \"slug\": \"hd-native\",\n" + + " \"type\": \"video\",\n" + + " \"urls\": {\n" + + " \"hls\": {\n" + + " \"display\": \"HLS\",\n" + + " \"tech\": \"1920x1080, h264+AAC im MPEG-TS-Container via HTTP, 3 MBit/s\",\n" + + " \"url\": \"https://cdn.c3voc.de/hls/s2_native_hd.m3u8\"\n" + + " },\n" + + " \"webm\": {\n" + + " \"display\": \"WebM\",\n" + + " \"tech\": \"1920x1080, VP8+Vorbis in WebM, 2.8 MBit/s\",\n" + + " \"url\": \"https://cdn.c3voc.de/s2_native_hd.webm\"\n" + + " }\n" + + " },\n" + + " \"videoSize\": [\n" + + " 1920,\n" + + " 1080\n" + + " ]\n" + + " },\n" + + " {\n" + + " \"display\": \"Saal 2 FullHD Video (Translation)\",\n" + + " \"isTranslated\": true,\n" + + " \"slug\": \"hd-translated\",\n" + + " \"type\": \"video\",\n" + + " \"urls\": {\n" + + " \"hls\": {\n" + + " \"display\": \"HLS\",\n" + + " \"tech\": \"1920x1080, h264+AAC im MPEG-TS-Container via HTTP, 3 MBit/s\",\n" + + " \"url\": \"https://cdn.c3voc.de/hls/s2_translated_hd.m3u8\"\n" + + " },\n" + + " \"webm\": {\n" + + " \"display\": \"WebM\",\n" + + " \"tech\": \"1920x1080, VP8+Vorbis in WebM, 2.8 MBit/s\",\n" + + " \"url\": \"https://cdn.c3voc.de/s2_translated_hd.webm\"\n" + + " }\n" + + " },\n" + + " \"videoSize\": [\n" + + " 1920,\n" + + " 1080\n" + + " ]\n" + + " },\n" + + " {\n" + + " \"display\": \"Saal 2 SD Video\",\n" + + " \"isTranslated\": false,\n" + + " \"slug\": \"sd-native\",\n" + + " \"type\": \"video\",\n" + + " \"urls\": {\n" + + " \"hls\": {\n" + + " \"display\": \"HLS\",\n" + + " \"tech\": \"1024x576, h264+AAC im MPEG-TS-Container via HTTP, 800 kBit/s\",\n" + + " \"url\": \"https://cdn.c3voc.de/hls/s2_native_sd.m3u8\"\n" + + " },\n" + + " \"webm\": {\n" + + " \"display\": \"WebM\",\n" + + " \"tech\": \"1024x576, VP8+Vorbis in WebM, 800 kBit/s\",\n" + + " \"url\": \"https://cdn.c3voc.de/s2_native_sd.webm\"\n" + + " }\n" + + " },\n" + + " \"videoSize\": [\n" + + " 1024,\n" + + " 576\n" + + " ]\n" + + " },\n" + + " {\n" + + " \"display\": \"Saal 2 SD Video (Translation)\",\n" + + " \"isTranslated\": true,\n" + + " \"slug\": \"sd-translated\",\n" + + " \"type\": \"video\",\n" + + " \"urls\": {\n" + + " \"hls\": {\n" + + " \"display\": \"HLS\",\n" + + " \"tech\": \"1024x576, h264+AAC im MPEG-TS-Container via HTTP, 800 kBit/s\",\n" + + " \"url\": \"https://cdn.c3voc.de/hls/s2_translated_sd.m3u8\"\n" + + " },\n" + + " \"webm\": {\n" + + " \"display\": \"WebM\",\n" + + " \"tech\": \"1024x576, VP8+Vorbis in WebM, 800 kBit/s\",\n" + + " \"url\": \"https://cdn.c3voc.de/s2_translated_sd.webm\"\n" + + " }\n" + + " },\n" + + " \"videoSize\": [\n" + + " 1024,\n" + + " 576\n" + + " ]\n" + + " },\n" + + " {\n" + + " \"display\": \"Saal 2 Slides\",\n" + + " \"isTranslated\": false,\n" + + " \"slug\": \"slides-native\",\n" + + " \"type\": \"slides\",\n" + + " \"urls\": {\n" + + " \"hls\": {\n" + + " \"display\": \"HLS\",\n" + + " \"tech\": \"1024x576, h264+AAC im MPEG-TS-Container via HTTP, XXX kBit/s\",\n" + + " \"url\": \"https://cdn.c3voc.de/hls/s2_native_slides.m3u8\"\n" + + " },\n" + + " \"webm\": {\n" + + " \"display\": \"WebM\",\n" + + " \"tech\": \"1024x576, VP8+Vorbis in WebM, XXX kBit/s\",\n" + + " \"url\": \"https://cdn.c3voc.de/s2_native_slides.webm\"\n" + + " }\n" + + " },\n" + + " \"videoSize\": [\n" + + " 1024,\n" + + " 576\n" + + " ]\n" + + " },\n" + + " {\n" + + " \"display\": \"Saal 2 Slides (Translation)\",\n" + + " \"isTranslated\": true,\n" + + " \"slug\": \"slides-translated\",\n" + + " \"type\": \"slides\",\n" + + " \"urls\": {\n" + + " \"hls\": {\n" + + " \"display\": \"HLS\",\n" + + " \"tech\": \"1024x576, h264+AAC im MPEG-TS-Container via HTTP, XXX kBit/s\",\n" + + " \"url\": \"https://cdn.c3voc.de/hls/s2_translated_slides.m3u8\"\n" + + " },\n" + + " \"webm\": {\n" + + " \"display\": \"WebM\",\n" + + " \"tech\": \"1024x576, VP8+Vorbis in WebM, XXX kBit/s\",\n" + + " \"url\": \"https://cdn.c3voc.de/s2_translated_slides.webm\"\n" + + " }\n" + + " },\n" + + " \"videoSize\": [\n" + + " 1024,\n" + + " 576\n" + + " ]\n" + + " },\n" + + " {\n" + + " \"display\": \"Saal 2 Audio\",\n" + + " \"isTranslated\": false,\n" + + " \"slug\": \"audio-native\",\n" + + " \"type\": \"audio\",\n" + + " \"urls\": {\n" + + " \"mp3\": {\n" + + " \"display\": \"MP3\",\n" + + " \"tech\": \"MP3-Audio, 96 kBit/s\",\n" + + " \"url\": \"https://cdn.c3voc.de/s2_native.mp3\"\n" + + " },\n" + + " \"opus\": {\n" + + " \"display\": \"Opus\",\n" + + " \"tech\": \"Opus-Audio, 64 kBit/s\",\n" + + " \"url\": \"https://cdn.c3voc.de/s2_native.opus\"\n" + + " }\n" + + " },\n" + + " \"videoSize\": null\n" + + " },\n" + + " {\n" + + " \"display\": \"Saal 2 Audio (Translation)\",\n" + + " \"isTranslated\": true,\n" + + " \"slug\": \"audio-translated\",\n" + + " \"type\": \"audio\",\n" + + " \"urls\": {\n" + + " \"mp3\": {\n" + + " \"display\": \"MP3\",\n" + + " \"tech\": \"MP3-Audio, 96 kBit/s\",\n" + + " \"url\": \"https://cdn.c3voc.de/s2_translated.mp3\"\n" + + " },\n" + + " \"opus\": {\n" + + " \"display\": \"Opus\",\n" + + " \"tech\": \"Opus-Audio, 64 kBit/s\",\n" + + " \"url\": \"https://cdn.c3voc.de/s2_translated.opus\"\n" + + " }\n" + + " },\n" + + " \"videoSize\": null\n" + + " }\n" + + " ],\n" + + " \"thumb\": \"https://streaming.media.ccc.de/thumbs/s2.png\"\n" + + " },\n" + + " {\n" + + " \"display\": \"Saal G\",\n" + + " \"link\": \"https://streaming.media.ccc.de/33c3/hallg\",\n" + + " \"schedulename\": \"Saal G\",\n" + + " \"slug\": \"hallg\",\n" + + " \"streams\": [\n" + + " {\n" + + " \"display\": \"Saal G FullHD Video\",\n" + + " \"isTranslated\": false,\n" + + " \"slug\": \"hd-native\",\n" + + " \"type\": \"video\",\n" + + " \"urls\": {\n" + + " \"hls\": {\n" + + " \"display\": \"HLS\",\n" + + " \"tech\": \"1920x1080, h264+AAC im MPEG-TS-Container via HTTP, 3 MBit/s\",\n" + + " \"url\": \"https://cdn.c3voc.de/hls/s3_native_hd.m3u8\"\n" + + " },\n" + + " \"webm\": {\n" + + " \"display\": \"WebM\",\n" + + " \"tech\": \"1920x1080, VP8+Vorbis in WebM, 2.8 MBit/s\",\n" + + " \"url\": \"https://cdn.c3voc.de/s3_native_hd.webm\"\n" + + " }\n" + + " },\n" + + " \"videoSize\": [\n" + + " 1920,\n" + + " 1080\n" + + " ]\n" + + " },\n" + + " {\n" + + " \"display\": \"Saal G FullHD Video (Translation)\",\n" + + " \"isTranslated\": true,\n" + + " \"slug\": \"hd-translated\",\n" + + " \"type\": \"video\",\n" + + " \"urls\": {\n" + + " \"hls\": {\n" + + " \"display\": \"HLS\",\n" + + " \"tech\": \"1920x1080, h264+AAC im MPEG-TS-Container via HTTP, 3 MBit/s\",\n" + + " \"url\": \"https://cdn.c3voc.de/hls/s3_translated_hd.m3u8\"\n" + + " },\n" + + " \"webm\": {\n" + + " \"display\": \"WebM\",\n" + + " \"tech\": \"1920x1080, VP8+Vorbis in WebM, 2.8 MBit/s\",\n" + + " \"url\": \"https://cdn.c3voc.de/s3_translated_hd.webm\"\n" + + " }\n" + + " },\n" + + " \"videoSize\": [\n" + + " 1920,\n" + + " 1080\n" + + " ]\n" + + " },\n" + + " {\n" + + " \"display\": \"Saal G SD Video\",\n" + + " \"isTranslated\": false,\n" + + " \"slug\": \"sd-native\",\n" + + " \"type\": \"video\",\n" + + " \"urls\": {\n" + + " \"hls\": {\n" + + " \"display\": \"HLS\",\n" + + " \"tech\": \"1024x576, h264+AAC im MPEG-TS-Container via HTTP, 800 kBit/s\",\n" + + " \"url\": \"https://cdn.c3voc.de/hls/s3_native_sd.m3u8\"\n" + + " },\n" + + " \"webm\": {\n" + + " \"display\": \"WebM\",\n" + + " \"tech\": \"1024x576, VP8+Vorbis in WebM, 800 kBit/s\",\n" + + " \"url\": \"https://cdn.c3voc.de/s3_native_sd.webm\"\n" + + " }\n" + + " },\n" + + " \"videoSize\": [\n" + + " 1024,\n" + + " 576\n" + + " ]\n" + + " },\n" + + " {\n" + + " \"display\": \"Saal G SD Video (Translation)\",\n" + + " \"isTranslated\": true,\n" + + " \"slug\": \"sd-translated\",\n" + + " \"type\": \"video\",\n" + + " \"urls\": {\n" + + " \"hls\": {\n" + + " \"display\": \"HLS\",\n" + + " \"tech\": \"1024x576, h264+AAC im MPEG-TS-Container via HTTP, 800 kBit/s\",\n" + + " \"url\": \"https://cdn.c3voc.de/hls/s3_translated_sd.m3u8\"\n" + + " },\n" + + " \"webm\": {\n" + + " \"display\": \"WebM\",\n" + + " \"tech\": \"1024x576, VP8+Vorbis in WebM, 800 kBit/s\",\n" + + " \"url\": \"https://cdn.c3voc.de/s3_translated_sd.webm\"\n" + + " }\n" + + " },\n" + + " \"videoSize\": [\n" + + " 1024,\n" + + " 576\n" + + " ]\n" + + " },\n" + + " {\n" + + " \"display\": \"Saal G Slides\",\n" + + " \"isTranslated\": false,\n" + + " \"slug\": \"slides-native\",\n" + + " \"type\": \"slides\",\n" + + " \"urls\": {\n" + + " \"hls\": {\n" + + " \"display\": \"HLS\",\n" + + " \"tech\": \"1024x576, h264+AAC im MPEG-TS-Container via HTTP, XXX kBit/s\",\n" + + " \"url\": \"https://cdn.c3voc.de/hls/s3_native_slides.m3u8\"\n" + + " },\n" + + " \"webm\": {\n" + + " \"display\": \"WebM\",\n" + + " \"tech\": \"1024x576, VP8+Vorbis in WebM, XXX kBit/s\",\n" + + " \"url\": \"https://cdn.c3voc.de/s3_native_slides.webm\"\n" + + " }\n" + + " },\n" + + " \"videoSize\": [\n" + + " 1024,\n" + + " 576\n" + + " ]\n" + + " },\n" + + " {\n" + + " \"display\": \"Saal G Slides (Translation)\",\n" + + " \"isTranslated\": true,\n" + + " \"slug\": \"slides-translated\",\n" + + " \"type\": \"slides\",\n" + + " \"urls\": {\n" + + " \"hls\": {\n" + + " \"display\": \"HLS\",\n" + + " \"tech\": \"1024x576, h264+AAC im MPEG-TS-Container via HTTP, XXX kBit/s\",\n" + + " \"url\": \"https://cdn.c3voc.de/hls/s3_translated_slides.m3u8\"\n" + + " },\n" + + " \"webm\": {\n" + + " \"display\": \"WebM\",\n" + + " \"tech\": \"1024x576, VP8+Vorbis in WebM, XXX kBit/s\",\n" + + " \"url\": \"https://cdn.c3voc.de/s3_translated_slides.webm\"\n" + + " }\n" + + " },\n" + + " \"videoSize\": [\n" + + " 1024,\n" + + " 576\n" + + " ]\n" + + " },\n" + + " {\n" + + " \"display\": \"Saal G Audio\",\n" + + " \"isTranslated\": false,\n" + + " \"slug\": \"audio-native\",\n" + + " \"type\": \"audio\",\n" + + " \"urls\": {\n" + + " \"mp3\": {\n" + + " \"display\": \"MP3\",\n" + + " \"tech\": \"MP3-Audio, 96 kBit/s\",\n" + + " \"url\": \"https://cdn.c3voc.de/s3_native.mp3\"\n" + + " },\n" + + " \"opus\": {\n" + + " \"display\": \"Opus\",\n" + + " \"tech\": \"Opus-Audio, 64 kBit/s\",\n" + + " \"url\": \"https://cdn.c3voc.de/s3_native.opus\"\n" + + " }\n" + + " },\n" + + " \"videoSize\": null\n" + + " },\n" + + " {\n" + + " \"display\": \"Saal G Audio (Translation)\",\n" + + " \"isTranslated\": true,\n" + + " \"slug\": \"audio-translated\",\n" + + " \"type\": \"audio\",\n" + + " \"urls\": {\n" + + " \"mp3\": {\n" + + " \"display\": \"MP3\",\n" + + " \"tech\": \"MP3-Audio, 96 kBit/s\",\n" + + " \"url\": \"https://cdn.c3voc.de/s3_translated.mp3\"\n" + + " },\n" + + " \"opus\": {\n" + + " \"display\": \"Opus\",\n" + + " \"tech\": \"Opus-Audio, 64 kBit/s\",\n" + + " \"url\": \"https://cdn.c3voc.de/s3_translated.opus\"\n" + + " }\n" + + " },\n" + + " \"videoSize\": null\n" + + " }\n" + + " ],\n" + + " \"thumb\": \"https://streaming.media.ccc.de/thumbs/s3.png\"\n" + + " },\n" + + " {\n" + + " \"display\": \"Saal 6\",\n" + + " \"link\": \"https://streaming.media.ccc.de/33c3/hall6\",\n" + + " \"schedulename\": \"Saal 6\",\n" + + " \"slug\": \"hall6\",\n" + + " \"streams\": [\n" + + " {\n" + + " \"display\": \"Saal 6 FullHD Video\",\n" + + " \"isTranslated\": false,\n" + + " \"slug\": \"hd-native\",\n" + + " \"type\": \"video\",\n" + + " \"urls\": {\n" + + " \"hls\": {\n" + + " \"display\": \"HLS\",\n" + + " \"tech\": \"1920x1080, h264+AAC im MPEG-TS-Container via HTTP, 3 MBit/s\",\n" + + " \"url\": \"https://cdn.c3voc.de/hls/s4_native_hd.m3u8\"\n" + + " },\n" + + " \"webm\": {\n" + + " \"display\": \"WebM\",\n" + + " \"tech\": \"1920x1080, VP8+Vorbis in WebM, 2.8 MBit/s\",\n" + + " \"url\": \"https://cdn.c3voc.de/s4_native_hd.webm\"\n" + + " }\n" + + " },\n" + + " \"videoSize\": [\n" + + " 1920,\n" + + " 1080\n" + + " ]\n" + + " },\n" + + " {\n" + + " \"display\": \"Saal 6 FullHD Video (Translation)\",\n" + + " \"isTranslated\": true,\n" + + " \"slug\": \"hd-translated\",\n" + + " \"type\": \"video\",\n" + + " \"urls\": {\n" + + " \"hls\": {\n" + + " \"display\": \"HLS\",\n" + + " \"tech\": \"1920x1080, h264+AAC im MPEG-TS-Container via HTTP, 3 MBit/s\",\n" + + " \"url\": \"https://cdn.c3voc.de/hls/s4_translated_hd.m3u8\"\n" + + " },\n" + + " \"webm\": {\n" + + " \"display\": \"WebM\",\n" + + " \"tech\": \"1920x1080, VP8+Vorbis in WebM, 2.8 MBit/s\",\n" + + " \"url\": \"https://cdn.c3voc.de/s4_translated_hd.webm\"\n" + + " }\n" + + " },\n" + + " \"videoSize\": [\n" + + " 1920,\n" + + " 1080\n" + + " ]\n" + + " },\n" + + " {\n" + + " \"display\": \"Saal 6 SD Video\",\n" + + " \"isTranslated\": false,\n" + + " \"slug\": \"sd-native\",\n" + + " \"type\": \"video\",\n" + + " \"urls\": {\n" + + " \"hls\": {\n" + + " \"display\": \"HLS\",\n" + + " \"tech\": \"1024x576, h264+AAC im MPEG-TS-Container via HTTP, 800 kBit/s\",\n" + + " \"url\": \"https://cdn.c3voc.de/hls/s4_native_sd.m3u8\"\n" + + " },\n" + + " \"webm\": {\n" + + " \"display\": \"WebM\",\n" + + " \"tech\": \"1024x576, VP8+Vorbis in WebM, 800 kBit/s\",\n" + + " \"url\": \"https://cdn.c3voc.de/s4_native_sd.webm\"\n" + + " }\n" + + " },\n" + + " \"videoSize\": [\n" + + " 1024,\n" + + " 576\n" + + " ]\n" + + " },\n" + + " {\n" + + " \"display\": \"Saal 6 SD Video (Translation)\",\n" + + " \"isTranslated\": true,\n" + + " \"slug\": \"sd-translated\",\n" + + " \"type\": \"video\",\n" + + " \"urls\": {\n" + + " \"hls\": {\n" + + " \"display\": \"HLS\",\n" + + " \"tech\": \"1024x576, h264+AAC im MPEG-TS-Container via HTTP, 800 kBit/s\",\n" + + " \"url\": \"https://cdn.c3voc.de/hls/s4_translated_sd.m3u8\"\n" + + " },\n" + + " \"webm\": {\n" + + " \"display\": \"WebM\",\n" + + " \"tech\": \"1024x576, VP8+Vorbis in WebM, 800 kBit/s\",\n" + + " \"url\": \"https://cdn.c3voc.de/s4_translated_sd.webm\"\n" + + " }\n" + + " },\n" + + " \"videoSize\": [\n" + + " 1024,\n" + + " 576\n" + + " ]\n" + + " },\n" + + " {\n" + + " \"display\": \"Saal 6 Slides\",\n" + + " \"isTranslated\": false,\n" + + " \"slug\": \"slides-native\",\n" + + " \"type\": \"slides\",\n" + + " \"urls\": {\n" + + " \"hls\": {\n" + + " \"display\": \"HLS\",\n" + + " \"tech\": \"1024x576, h264+AAC im MPEG-TS-Container via HTTP, XXX kBit/s\",\n" + + " \"url\": \"https://cdn.c3voc.de/hls/s4_native_slides.m3u8\"\n" + + " },\n" + + " \"webm\": {\n" + + " \"display\": \"WebM\",\n" + + " \"tech\": \"1024x576, VP8+Vorbis in WebM, XXX kBit/s\",\n" + + " \"url\": \"https://cdn.c3voc.de/s4_native_slides.webm\"\n" + + " }\n" + + " },\n" + + " \"videoSize\": [\n" + + " 1024,\n" + + " 576\n" + + " ]\n" + + " },\n" + + " {\n" + + " \"display\": \"Saal 6 Slides (Translation)\",\n" + + " \"isTranslated\": true,\n" + + " \"slug\": \"slides-translated\",\n" + + " \"type\": \"slides\",\n" + + " \"urls\": {\n" + + " \"hls\": {\n" + + " \"display\": \"HLS\",\n" + + " \"tech\": \"1024x576, h264+AAC im MPEG-TS-Container via HTTP, XXX kBit/s\",\n" + + " \"url\": \"https://cdn.c3voc.de/hls/s4_translated_slides.m3u8\"\n" + + " },\n" + + " \"webm\": {\n" + + " \"display\": \"WebM\",\n" + + " \"tech\": \"1024x576, VP8+Vorbis in WebM, XXX kBit/s\",\n" + + " \"url\": \"https://cdn.c3voc.de/s4_translated_slides.webm\"\n" + + " }\n" + + " },\n" + + " \"videoSize\": [\n" + + " 1024,\n" + + " 576\n" + + " ]\n" + + " },\n" + + " {\n" + + " \"display\": \"Saal 6 Audio\",\n" + + " \"isTranslated\": false,\n" + + " \"slug\": \"audio-native\",\n" + + " \"type\": \"audio\",\n" + + " \"urls\": {\n" + + " \"mp3\": {\n" + + " \"display\": \"MP3\",\n" + + " \"tech\": \"MP3-Audio, 96 kBit/s\",\n" + + " \"url\": \"https://cdn.c3voc.de/s4_native.mp3\"\n" + + " },\n" + + " \"opus\": {\n" + + " \"display\": \"Opus\",\n" + + " \"tech\": \"Opus-Audio, 64 kBit/s\",\n" + + " \"url\": \"https://cdn.c3voc.de/s4_native.opus\"\n" + + " }\n" + + " },\n" + + " \"videoSize\": null\n" + + " },\n" + + " {\n" + + " \"display\": \"Saal 6 Audio (Translation)\",\n" + + " \"isTranslated\": true,\n" + + " \"slug\": \"audio-translated\",\n" + + " \"type\": \"audio\",\n" + + " \"urls\": {\n" + + " \"mp3\": {\n" + + " \"display\": \"MP3\",\n" + + " \"tech\": \"MP3-Audio, 96 kBit/s\",\n" + + " \"url\": \"https://cdn.c3voc.de/s4_translated.mp3\"\n" + + " },\n" + + " \"opus\": {\n" + + " \"display\": \"Opus\",\n" + + " \"tech\": \"Opus-Audio, 64 kBit/s\",\n" + + " \"url\": \"https://cdn.c3voc.de/s4_translated.opus\"\n" + + " }\n" + + " },\n" + + " \"videoSize\": null\n" + + " }\n" + + " ],\n" + + " \"thumb\": \"https://streaming.media.ccc.de/thumbs/s4.png\"\n" + + " },\n" + + " {\n" + + " \"display\": \"Sendezentrum\",\n" + + " \"link\": \"https://streaming.media.ccc.de/33c3/sendezentrum\",\n" + + " \"schedulename\": \"Sendezentrum\",\n" + + " \"slug\": \"sendezentrum\",\n" + + " \"streams\": [\n" + + " {\n" + + " \"display\": \"Sendezentrum FullHD Video\",\n" + + " \"isTranslated\": false,\n" + + " \"slug\": \"hd-native\",\n" + + " \"type\": \"video\",\n" + + " \"urls\": {\n" + + " \"hls\": {\n" + + " \"display\": \"HLS\",\n" + + " \"tech\": \"1920x1080, h264+AAC im MPEG-TS-Container via HTTP, 3 MBit/s\",\n" + + " \"url\": \"https://cdn.c3voc.de/hls/s5_native_hd.m3u8\"\n" + + " },\n" + + " \"webm\": {\n" + + " \"display\": \"WebM\",\n" + + " \"tech\": \"1920x1080, VP8+Vorbis in WebM, 2.8 MBit/s\",\n" + + " \"url\": \"https://cdn.c3voc.de/s5_native_hd.webm\"\n" + + " }\n" + + " },\n" + + " \"videoSize\": [\n" + + " 1920,\n" + + " 1080\n" + + " ]\n" + + " },\n" + + " {\n" + + " \"display\": \"Sendezentrum SD Video\",\n" + + " \"isTranslated\": false,\n" + + " \"slug\": \"sd-native\",\n" + + " \"type\": \"video\",\n" + + " \"urls\": {\n" + + " \"hls\": {\n" + + " \"display\": \"HLS\",\n" + + " \"tech\": \"1024x576, h264+AAC im MPEG-TS-Container via HTTP, 800 kBit/s\",\n" + + " \"url\": \"https://cdn.c3voc.de/hls/s5_native_sd.m3u8\"\n" + + " },\n" + + " \"webm\": {\n" + + " \"display\": \"WebM\",\n" + + " \"tech\": \"1024x576, VP8+Vorbis in WebM, 800 kBit/s\",\n" + + " \"url\": \"https://cdn.c3voc.de/s5_native_sd.webm\"\n" + + " }\n" + + " },\n" + + " \"videoSize\": [\n" + + " 1024,\n" + + " 576\n" + + " ]\n" + + " },\n" + + " {\n" + + " \"display\": \"Sendezentrum Audio\",\n" + + " \"isTranslated\": false,\n" + + " \"slug\": \"audio-native\",\n" + + " \"type\": \"audio\",\n" + + " \"urls\": {\n" + + " \"mp3\": {\n" + + " \"display\": \"MP3\",\n" + + " \"tech\": \"MP3-Audio, 96 kBit/s\",\n" + + " \"url\": \"https://cdn.c3voc.de/s5_native.mp3\"\n" + + " },\n" + + " \"opus\": {\n" + + " \"display\": \"Opus\",\n" + + " \"tech\": \"Opus-Audio, 64 kBit/s\",\n" + + " \"url\": \"https://cdn.c3voc.de/s5_native.opus\"\n" + + " }\n" + + " },\n" + + " \"videoSize\": null\n" + + " }\n" + + " ],\n" + + " \"thumb\": \"https://streaming.media.ccc.de/thumbs/s5.png\"\n" + + " }\n" + + " ]\n" + + " },\n" + + " {\n" + + " \"group\": \"Live Music\",\n" + + " \"rooms\": [\n" + + " {\n" + + " \"display\": \"Section 9\",\n" + + " \"link\": \"https://streaming.media.ccc.de/33c3/section-9\",\n" + + " \"schedulename\": \"Section 9\",\n" + + " \"slug\": \"section-9\",\n" + + " \"streams\": [\n" + + " {\n" + + " \"display\": \"Section 9 Radio\",\n" + + " \"isTranslated\": false,\n" + + " \"slug\": \"music-native\",\n" + + " \"type\": \"music\",\n" + + " \"urls\": {\n" + + " \"mp3\": {\n" + + " \"display\": \"MP3\",\n" + + " \"tech\": \"MP3-Audio, 192 kBit/s\",\n" + + " \"url\": \"https://cdn.c3voc.de/ambient.mp3\"\n" + + " },\n" + + " \"opus\": {\n" + + " \"display\": \"Opus\",\n" + + " \"tech\": \"Opus-Audio, 96 kBit/s\",\n" + + " \"url\": \"https://cdn.c3voc.de/ambient.opus\"\n" + + " }\n" + + " },\n" + + " \"videoSize\": null\n" + + " }\n" + + " ],\n" + + " \"thumb\": \"https://streaming.media.ccc.de/thumbs/ambient.png\"\n" + + " },\n" + + " {\n" + + " \"display\": \"DisKo\",\n" + + " \"link\": \"https://streaming.media.ccc.de/33c3/disko\",\n" + + " \"schedulename\": \"DisKo\",\n" + + " \"slug\": \"disko\",\n" + + " \"streams\": [\n" + + " {\n" + + " \"display\": \"DisKo Radio\",\n" + + " \"isTranslated\": false,\n" + + " \"slug\": \"music-native\",\n" + + " \"type\": \"music\",\n" + + " \"urls\": {\n" + + " \"mp3\": {\n" + + " \"display\": \"MP3\",\n" + + " \"tech\": \"MP3-Audio, 192 kBit/s\",\n" + + " \"url\": \"https://cdn.c3voc.de/lounge.mp3\"\n" + + " },\n" + + " \"opus\": {\n" + + " \"display\": \"Opus\",\n" + + " \"tech\": \"Opus-Audio, 96 kBit/s\",\n" + + " \"url\": \"https://cdn.c3voc.de/lounge.opus\"\n" + + " }\n" + + " },\n" + + " \"videoSize\": null\n" + + " }\n" + + " ],\n" + + " \"thumb\": \"https://streaming.media.ccc.de/thumbs/lounge.png\"\n" + + " },\n" + + " {\n" + + " \"display\": \"Schneekugel\",\n" + + " \"link\": \"https://streaming.media.ccc.de/33c3/schneekugel\",\n" + + " \"schedulename\": \"Schneekugel\",\n" + + " \"slug\": \"schneekugel\",\n" + + " \"streams\": [\n" + + " {\n" + + " \"display\": \"Schneekugel Radio\",\n" + + " \"isTranslated\": false,\n" + + " \"slug\": \"music-native\",\n" + + " \"type\": \"music\",\n" + + " \"urls\": {\n" + + " \"mp3\": {\n" + + " \"display\": \"MP3\",\n" + + " \"tech\": \"MP3-Audio, 192 kBit/s\",\n" + + " \"url\": \"https://cdn.c3voc.de/dome.mp3\"\n" + + " },\n" + + " \"opus\": {\n" + + " \"display\": \"Opus\",\n" + + " \"tech\": \"Opus-Audio, 96 kBit/s\",\n" + + " \"url\": \"https://cdn.c3voc.de/dome.opus\"\n" + + " }\n" + + " },\n" + + " \"videoSize\": null\n" + + " }\n" + + " ],\n" + + " \"thumb\": \"https://streaming.media.ccc.de/thumbs/dome.png\"\n" + + " },\n" + + " {\n" + + " \"display\": \"Chaoswest\",\n" + + " \"link\": \"https://streaming.media.ccc.de/33c3/chaoswest\",\n" + + " \"schedulename\": \"Chaoswest\",\n" + + " \"slug\": \"chaoswest\",\n" + + " \"streams\": [\n" + + " {\n" + + " \"display\": \"Chaoswest Radio\",\n" + + " \"isTranslated\": false,\n" + + " \"slug\": \"music-native\",\n" + + " \"type\": \"music\",\n" + + " \"urls\": {\n" + + " \"mp3\": {\n" + + " \"display\": \"MP3\",\n" + + " \"tech\": \"MP3-Audio, 192 kBit/s\",\n" + + " \"url\": \"https://cdn.c3voc.de/chaoswest_lounge.mp3\"\n" + + " },\n" + + " \"opus\": {\n" + + " \"display\": \"Opus\",\n" + + " \"tech\": \"Opus-Audio, 96 kBit/s\",\n" + + " \"url\": \"https://cdn.c3voc.de/chaoswest_lounge.opus\"\n" + + " }\n" + + " },\n" + + " \"videoSize\": null\n" + + " }\n" + + " ],\n" + + " \"thumb\": \"https://streaming.media.ccc.de/thumbs/chaoswest_lounge.png\"\n" + + " }\n" + + " ]\n" + + " }\n" + + " ],\n" + + " \"keywords\": \"33C3, Hacking, Chaos Computer Club, Video, Music, Podcast, Media, Streaming, Hacker, Hamburg, Works, For, Me, Chaos, Everywhere\",\n" + + " \"slug\": \"33c3\",\n" + + " \"startsAt\": \"2016-12-27T05:00:00+0000\"\n" + + " }\n" + + "]\n";*/ + try { + doc = JsonParser.array().from(site); + } catch (JsonParserException jpe) { + throw new ExtractionException("Could not parse json.", jpe); + } + } + + @Nonnull + @Override + public InfoItemsPage getInitialPage() throws IOException, ExtractionException { + StreamInfoItemsCollector collector = new StreamInfoItemsCollector(getServiceId()); + for (int c = 0; c < doc.size(); c++) { + final JsonObject conference = doc.getObject(c); + final JsonArray groups = conference.getArray("groups"); + for (int g = 0; g < groups.size(); g++) { + final String group = groups.getObject(g).getString("group"); + final JsonArray rooms = groups.getObject(g).getArray("rooms"); + for (int r = 0; r < rooms.size(); r++) { + final JsonObject room = rooms.getObject(r); + collector.commit(new MediaCCCLiveStreamExtractor(conference, group, room)); + } + } + + } + return new InfoItemsPage<>(collector, null); + } + + @Override + public InfoItemsPage getPage(Page page) throws IOException, ExtractionException { + return InfoItemsPage.emptyPage(); + } + + @Nonnull + @Override + public String getName() throws ParsingException { + return "live"; + } +} diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/linkHandler/MediaCCCLiveStreamListLinkHandlerFactory.java b/extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/linkHandler/MediaCCCLiveStreamListLinkHandlerFactory.java new file mode 100644 index 000000000..3dc5c46ea --- /dev/null +++ b/extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/linkHandler/MediaCCCLiveStreamListLinkHandlerFactory.java @@ -0,0 +1,27 @@ +package org.schabi.newpipe.extractor.services.media_ccc.linkHandler; + +import org.schabi.newpipe.extractor.exceptions.ParsingException; +import org.schabi.newpipe.extractor.linkhandler.ListLinkHandlerFactory; + +import java.util.List; +import java.util.regex.Pattern; + +public class MediaCCCLiveStreamListLinkHandlerFactory extends ListLinkHandlerFactory { + private static final String streamPattern = "^(https?://)?streaming.media.ccc.de$"; + + @Override + public String getId(String url) throws ParsingException { + return "live"; + } + + @Override + public boolean onAcceptUrl(String url) throws ParsingException { + return Pattern.matches(streamPattern, url); + } + + @Override + public String getUrl(String id, List contentFilter, String sortFilter) throws ParsingException { + // FIXME: wrong URL; should be https://streaming.media.ccc.de/{conference_slug}/{room_slug} + return "https://streaming.media.ccc.de/" + id; + } +} diff --git a/extractor/src/test/java/org/schabi/newpipe/extractor/services/media_ccc/MediaCCCLiveStreamListExtractorTest.java b/extractor/src/test/java/org/schabi/newpipe/extractor/services/media_ccc/MediaCCCLiveStreamListExtractorTest.java new file mode 100644 index 000000000..1cc75326e --- /dev/null +++ b/extractor/src/test/java/org/schabi/newpipe/extractor/services/media_ccc/MediaCCCLiveStreamListExtractorTest.java @@ -0,0 +1,39 @@ +package org.schabi.newpipe.extractor.services.media_ccc; + +import org.junit.BeforeClass; +import org.junit.Test; +import org.schabi.newpipe.DownloaderTestImpl; +import org.schabi.newpipe.extractor.InfoItem; +import org.schabi.newpipe.extractor.ListExtractor; +import org.schabi.newpipe.extractor.NewPipe; +import org.schabi.newpipe.extractor.kiosk.KioskExtractor; + +import java.util.List; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.schabi.newpipe.extractor.ServiceList.MediaCCC; + +public class MediaCCCLiveStreamListExtractorTest { + private static KioskExtractor extractor; + + @BeforeClass + public static void setUpClass() throws Exception { + NewPipe.init(DownloaderTestImpl.getInstance()); + extractor = MediaCCC.getKioskList().getExtractorById("live", null); + extractor.fetchPage(); + } + + + @Test + public void getConferencesListTest() throws Exception { + final List a = extractor.getInitialPage().getItems(); + assertTrue(a.size() > 2); + for (int i = 0; i < a.size(); i++) { + final InfoItem b = a.get(i); + assertNotNull(a.get(i).getName()); + assertTrue(a.get(i).getName().length() >= 1); + } + } + +} From b4e191397117447364d137791ec4b1ca3672d75b Mon Sep 17 00:00:00 2001 From: TobiGr Date: Sun, 27 Dec 2020 13:25:48 +0100 Subject: [PATCH 10/10] [media.ccc.de] Play live streams --- .../services/media_ccc/MediaCCCService.java | 9 +- .../MediaCCCLiveStreamExtractor.java | 324 ++- .../extractors/MediaCCCLiveStreamKiosk.java | 1918 +---------------- .../MediaCCCLiveStreamKioskExtractor.java | 87 + .../extractors/MediaCCCParsingHelper.java | 32 + ...> MediaCCCLiveListLinkHandlerFactory.java} | 6 +- .../MediaCCCLiveStreamLinkHandlerFactory.java | 30 + .../MediaCCCStreamLinkHandlerFactory.java | 20 +- 8 files changed, 446 insertions(+), 1980 deletions(-) create mode 100644 extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/extractors/MediaCCCLiveStreamKioskExtractor.java rename extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/linkHandler/{MediaCCCLiveStreamListLinkHandlerFactory.java => MediaCCCLiveListLinkHandlerFactory.java} (76%) create mode 100644 extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/linkHandler/MediaCCCLiveStreamLinkHandlerFactory.java diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/MediaCCCService.java b/extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/MediaCCCService.java index e7f41b313..02820c03a 100644 --- a/extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/MediaCCCService.java +++ b/extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/MediaCCCService.java @@ -20,8 +20,6 @@ import org.schabi.newpipe.extractor.stream.StreamExtractor; import org.schabi.newpipe.extractor.subscription.SubscriptionExtractor; import org.schabi.newpipe.extractor.suggestion.SuggestionExtractor; -import java.io.IOException; - import static java.util.Arrays.asList; import static org.schabi.newpipe.extractor.StreamingService.ServiceInfo.MediaCapability.AUDIO; import static org.schabi.newpipe.extractor.StreamingService.ServiceInfo.MediaCapability.VIDEO; @@ -58,6 +56,9 @@ public class MediaCCCService extends StreamingService { @Override public StreamExtractor getStreamExtractor(final LinkHandler linkHandler) { + if (MediaCCCParsingHelper.isLiveStreamId(linkHandler.getId())) { + return new MediaCCCLiveStreamExtractor(this, linkHandler); + } return new MediaCCCStreamExtractor(this, linkHandler); } @@ -108,9 +109,9 @@ public class MediaCCCService extends StreamingService { final String url, final String kioskId) throws ExtractionException { return new MediaCCCLiveStreamKiosk(MediaCCCService.this, - new MediaCCCLiveStreamListLinkHandlerFactory().fromUrl(url), kioskId); + new MediaCCCLiveListLinkHandlerFactory().fromUrl(url), kioskId); } - }, new MediaCCCLiveStreamListLinkHandlerFactory(), "live"); + }, new MediaCCCLiveListLinkHandlerFactory(), "live"); list.setDefaultKiosk("recent"); } catch (Exception e) { diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/extractors/MediaCCCLiveStreamExtractor.java b/extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/extractors/MediaCCCLiveStreamExtractor.java index c2f6e5ff0..605947946 100644 --- a/extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/extractors/MediaCCCLiveStreamExtractor.java +++ b/extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/extractors/MediaCCCLiveStreamExtractor.java @@ -1,75 +1,63 @@ package org.schabi.newpipe.extractor.services.media_ccc.extractors; +import com.grack.nanojson.JsonArray; import com.grack.nanojson.JsonObject; +import org.schabi.newpipe.extractor.MediaFormat; +import org.schabi.newpipe.extractor.MetaInfo; +import org.schabi.newpipe.extractor.StreamingService; +import org.schabi.newpipe.extractor.downloader.Downloader; +import org.schabi.newpipe.extractor.exceptions.ExtractionException; import org.schabi.newpipe.extractor.exceptions.ParsingException; +import org.schabi.newpipe.extractor.linkhandler.LinkHandler; import org.schabi.newpipe.extractor.localization.DateWrapper; -import org.schabi.newpipe.extractor.stream.StreamInfoItemExtractor; -import org.schabi.newpipe.extractor.stream.StreamType; +import org.schabi.newpipe.extractor.stream.*; +import javax.annotation.Nonnull; import javax.annotation.Nullable; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Locale; -public class MediaCCCLiveStreamExtractor implements StreamInfoItemExtractor { +public class MediaCCCLiveStreamExtractor extends StreamExtractor { + private JsonArray doc = null; + private JsonObject conference = null; + private String group = ""; + private JsonObject room = null; - private final JsonObject conferenceInfo; - private final String group; - private final JsonObject roomInfo; - - public MediaCCCLiveStreamExtractor(JsonObject conferenceInfo, String group, JsonObject roomInfo) { - this.conferenceInfo = conferenceInfo; - this.group = group; - this.roomInfo = roomInfo; + public MediaCCCLiveStreamExtractor(StreamingService service, LinkHandler linkHandler) { + super(service, linkHandler); } @Override - public String getName() throws ParsingException { - return roomInfo.getString("schedulename"); - } - - @Override - public String getUrl() throws ParsingException { - return roomInfo.getString("link"); - } - - @Override - public String getThumbnailUrl() throws ParsingException { - return roomInfo.getString("thumb"); - } - - @Override - public StreamType getStreamType() throws ParsingException { - boolean isVideo = false; - for (Object stream : roomInfo.getArray("streams")) { - if ("video".equals(((JsonObject) stream).getString("type"))) { - isVideo = true; - break; + public void onFetchPage(@Nonnull Downloader downloader) throws IOException, ExtractionException { + doc = MediaCCCParsingHelper.getLiveStreams(downloader, getExtractorLocalization()); + // find correct room + for (int c = 0; c < doc.size(); c++) { + final JsonObject conference = doc.getObject(c); + final JsonArray groups = conference.getArray("groups"); + for (int g = 0; g < groups.size(); g++) { + final String group = groups.getObject(g).getString("group"); + final JsonArray rooms = groups.getObject(g).getArray("rooms"); + for (int r = 0; r < rooms.size(); r++) { + final JsonObject room = rooms.getObject(r); + if (getId().equals(conference.getString("slug") + "/" + room.getString("slug"))) { + this.conference = conference; + this.group = group; + this.room = room; + return; + } + } } } - return isVideo ? StreamType.LIVE_STREAM : StreamType.AUDIO_LIVE_STREAM; + throw new ExtractionException("Could not find room matching id: '" + getId() + "'"); } + @Nonnull @Override - public boolean isAd() throws ParsingException { - return false; - } - - @Override - public long getDuration() throws ParsingException { - return 0; - } - - @Override - public long getViewCount() throws ParsingException { - return -1; - } - - @Override - public String getUploaderName() throws ParsingException { - return conferenceInfo.getString("conference"); - } - - @Override - public String getUploaderUrl() throws ParsingException { - return "https://media.ccc.de/c/" + conferenceInfo.getString("slug"); + public String getName() throws ParsingException { + return room.getString("display"); } @Nullable @@ -83,4 +71,230 @@ public class MediaCCCLiveStreamExtractor implements StreamInfoItemExtractor { public DateWrapper getUploadDate() throws ParsingException { return null; } + + @Nonnull + @Override + public String getThumbnailUrl() throws ParsingException { + return room.getString("thumb"); + } + + @Nonnull + @Override + public Description getDescription() throws ParsingException { + return new Description(conference.getString("description") + " - " + group, Description.PLAIN_TEXT); + } + + @Override + public int getAgeLimit() { + return 0; + } + + @Override + public long getLength() { + return 0; + } + + @Override + public long getTimeStamp() throws ParsingException { + return 0; + } + + @Override + public long getViewCount() { + return -1; + } + + @Override + public long getLikeCount() { + return -1; + } + + @Override + public long getDislikeCount() { + return -1; + } + + @Nonnull + @Override + public String getUploaderUrl() throws ParsingException { + return "https://streaming.media.ccc.de/" + conference.getString("slug"); + } + + @Nonnull + @Override + public String getUploaderName() throws ParsingException { + return conference.getString("conference"); + } + + @Nonnull + @Override + public String getUploaderAvatarUrl() { + return ""; + } + + @Nonnull + @Override + public String getSubChannelUrl() { + return ""; + } + + @Nonnull + @Override + public String getSubChannelName() { + return ""; + } + + @Nonnull + @Override + public String getSubChannelAvatarUrl() { + return ""; + } + + @Nonnull + @Override + public String getDashMpdUrl() throws ParsingException { + return ""; + } + + @Nonnull + @Override + public String getHlsUrl() { + // TODO: There are multiple HLS streams. + // Make getHlsUrl() and getDashMpdUrl() return lists of VideoStreams, so the user can choose a resolution. + for (int s = 0; s < room.getArray("streams").size(); s++) { + final JsonObject stream = room.getArray("streams").getObject(s); + if (stream.getString("type").equals("video")) { + final String resolution = stream.getArray("videoSize").getInt(0) + "x" + + stream.getArray("videoSize").getInt(1); + if (stream.has("hls")) { + return stream.getObject("urls").getObject("hls").getString("url"); + } + } + } + return ""; + } + + @Override + public List getAudioStreams() throws IOException, ExtractionException { + final List audioStreams = new ArrayList<>(); + for (int s = 0; s < room.getArray("streams").size(); s++) { + final JsonObject stream = room.getArray("streams").getObject(s); + if (stream.getString("type").equals("audio")) { + for (final String type :stream.getObject("urls").keySet()) { + final JsonObject url = stream.getObject("urls").getObject(type); + audioStreams.add(new AudioStream(url.getString("url"), MediaFormat.getFromSuffix(type), -1)); + } + } + } + return audioStreams; + } + + @Override + public List getVideoStreams() throws IOException, ExtractionException { + final List videoStreams = new ArrayList<>(); + for (int s = 0; s < room.getArray("streams").size(); s++) { + final JsonObject stream = room.getArray("streams").getObject(s); + if (stream.getString("type").equals("video")) { + final String resolution = stream.getArray("videoSize").getInt(0) + "x" + + stream.getArray("videoSize").getInt(1); + for (final String type :stream.getObject("urls").keySet()) { + if (!type.equals("hls")) { + final JsonObject url = stream.getObject("urls").getObject(type); + videoStreams.add(new VideoStream( + url.getString("url"), + MediaFormat.getFromSuffix(type), + resolution)); + } + } + } + } + return videoStreams; + } + + @Override + public List getVideoOnlyStreams() throws IOException, ExtractionException { + return null; + } + + @Nonnull + @Override + public List getSubtitlesDefault(){ + return Collections.emptyList(); + } + + @Nonnull + @Override + public List getSubtitles(MediaFormat format) { + return Collections.emptyList(); + } + + @Override + public StreamType getStreamType() throws ParsingException { + return StreamType.LIVE_STREAM; // TODO: video and audio only streams are both available + } + + @Nullable + @Override + public StreamInfoItemsCollector getRelatedStreams() { + return null; + } + + @Override + public String getErrorMessage() { + return null; + } + + @Nonnull + @Override + public String getHost() throws ParsingException { + return null; + } + + @Nonnull + @Override + public String getPrivacy() { + return "Public"; + } + + @Nonnull + @Override + public String getCategory() { + return group; + } + + @Nonnull + @Override + public String getLicence() { + return ""; + } + + @Nullable + @Override + public Locale getLanguageInfo() { + return null; + } + + @Nonnull + @Override + public List getTags() { + return Collections.emptyList(); + } + + @Nonnull + @Override + public String getSupportInfo() { + return ""; + } + + @Nonnull + @Override + public List getStreamSegments() { + return Collections.emptyList(); + } + + @Nonnull + @Override + public List getMetaInfo() { + return Collections.emptyList(); + } } diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/extractors/MediaCCCLiveStreamKiosk.java b/extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/extractors/MediaCCCLiveStreamKiosk.java index 5e6a9945f..cbaba6613 100644 --- a/extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/extractors/MediaCCCLiveStreamKiosk.java +++ b/extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/extractors/MediaCCCLiveStreamKiosk.java @@ -26,1921 +26,7 @@ public class MediaCCCLiveStreamKiosk extends KioskExtractor { @Override public void onFetchPage(@Nonnull Downloader downloader) throws IOException, ExtractionException { - String site = downloader.get("https://streaming.media.ccc.de/streams/v2.json", - getExtractorLocalization()).responseBody(); - // for testing, from - /*site = "[\n" + - " {\n" + - " \"conference\": \"32C3\",\n" + - " \"slug\": \"32c3\",\n" + - " \"author\": \"CCC\",\n" + - " \"description\": \"Live-Streaming vom 32C3\",\n" + - " \"keywords\": \"32C3, Hacking, Chaos Computer Club, Video, Music, Podcast, Media, Streaming, Hacker, Hamburg\",\n" + - " \"startsAt\": \"2015-12-27T05:00:00+0000\",\n" + - " \"endsAt\": \"2015-12-30T20:00:00+0000\",\n" + - " \"groups\": [\n" + - " {\n" + - " \"group\": \"Lecture Rooms\",\n" + - " \"rooms\": [\n" + - " {\n" + - " \"slug\": \"hall1\",\n" + - " \"schedulename\": \"Hall 1\",\n" + - " \"thumb\": \"https://static.media.ccc.de/media/conferences/archconf/2020/6390-hd.jpg\",\n" + - " \"link\": \"http://localhost:8000/streams/32c3/hall1?forceopen=yess\",\n" + - " \"display\": \"Hall 1\",\n" + - " \"streams\": [\n" + - " {\n" + - " \"slug\": \"hd-native\",\n" + - " \"display\": \"Hall 1 FullHD Video\",\n" + - " \"type\": \"video\",\n" + - " \"isTranslated\": false,\n" + - " \"videoSize\": [\n" + - " 1920,\n" + - " 1080\n" + - " ],\n" + - " \"urls\": {\n" + - " \"webm\": {\n" + - " \"display\": \"WebM\",\n" + - " \"tech\": \"1920x1080, VP8+Vorbis in WebM, 2.8 MBit/s\",\n" + - " \"url\": \"http://cdn.c3voc.de/s1_native_hd.webm\"\n" + - " },\n" + - " \"hls\": {\n" + - " \"display\": \"HLS\",\n" + - " \"tech\": \"1920x1080, h264+AAC im MPEG-TS-Container via HTTP, 3 MBit/s\",\n" + - " \"url\": \"http://cdn.c3voc.de/hls/s1_native_hd.m3u8\"\n" + - " }\n" + - " }\n" + - " },\n" + - " {\n" + - " \"slug\": \"hd-translated\",\n" + - " \"display\": \"Hall 1 FullHD Video (Translation)\",\n" + - " \"type\": \"video\",\n" + - " \"isTranslated\": true,\n" + - " \"videoSize\": [\n" + - " 1920,\n" + - " 1080\n" + - " ],\n" + - " \"urls\": {\n" + - " \"webm\": {\n" + - " \"display\": \"WebM\",\n" + - " \"tech\": \"1920x1080, VP8+Vorbis in WebM, 2.8 MBit/s\",\n" + - " \"url\": \"http://cdn.c3voc.de/s1_translated_hd.webm\"\n" + - " },\n" + - " \"hls\": {\n" + - " \"display\": \"HLS\",\n" + - " \"tech\": \"1920x1080, h264+AAC im MPEG-TS-Container via HTTP, 3 MBit/s\",\n" + - " \"url\": \"http://cdn.c3voc.de/hls/s1_translated_hd.m3u8\"\n" + - " }\n" + - " }\n" + - " },\n" + - " {\n" + - " \"slug\": \"sd-native\",\n" + - " \"display\": \"Hall 1 SD Video\",\n" + - " \"type\": \"video\",\n" + - " \"isTranslated\": false,\n" + - " \"videoSize\": [\n" + - " 1024,\n" + - " 576\n" + - " ],\n" + - " \"urls\": {\n" + - " \"webm\": {\n" + - " \"display\": \"WebM\",\n" + - " \"tech\": \"1024x576, VP8+Vorbis in WebM, 800 kBit/s\",\n" + - " \"url\": \"http://cdn.c3voc.de/s1_native_sd.webm\"\n" + - " },\n" + - " \"hls\": {\n" + - " \"display\": \"HLS\",\n" + - " \"tech\": \"1024x576, h264+AAC im MPEG-TS-Container via HTTP, 800 kBit/s\",\n" + - " \"url\": \"http://cdn.c3voc.de/hls/s1_native_sd.m3u8\"\n" + - " }\n" + - " }\n" + - " },\n" + - " {\n" + - " \"slug\": \"sd-translated\",\n" + - " \"display\": \"Hall 1 SD Video (Translation)\",\n" + - " \"type\": \"video\",\n" + - " \"isTranslated\": true,\n" + - " \"videoSize\": [\n" + - " 1024,\n" + - " 576\n" + - " ],\n" + - " \"urls\": {\n" + - " \"webm\": {\n" + - " \"display\": \"WebM\",\n" + - " \"tech\": \"1024x576, VP8+Vorbis in WebM, 800 kBit/s\",\n" + - " \"url\": \"http://cdn.c3voc.de/s1_translated_sd.webm\"\n" + - " },\n" + - " \"hls\": {\n" + - " \"display\": \"HLS\",\n" + - " \"tech\": \"1024x576, h264+AAC im MPEG-TS-Container via HTTP, 800 kBit/s\",\n" + - " \"url\": \"http://cdn.c3voc.de/hls/s1_translated_sd.m3u8\"\n" + - " }\n" + - " }\n" + - " },\n" + - " {\n" + - " \"slug\": \"slides-native\",\n" + - " \"display\": \"Hall 1 Slides\",\n" + - " \"type\": \"slides\",\n" + - " \"isTranslated\": false,\n" + - " \"videoSize\": [\n" + - " 1024,\n" + - " 576\n" + - " ],\n" + - " \"urls\": {\n" + - " \"webm\": {\n" + - " \"display\": \"WebM\",\n" + - " \"tech\": \"1024x576, VP8+Vorbis in WebM, XXX kBit/s\",\n" + - " \"url\": \"http://cdn.c3voc.de/s1_native_slides.webm\"\n" + - " },\n" + - " \"hls\": {\n" + - " \"display\": \"HLS\",\n" + - " \"tech\": \"1024x576, h264+AAC im MPEG-TS-Container via HTTP, XXX kBit/s\",\n" + - " \"url\": \"http://cdn.c3voc.de/hls/s1_native_slides.m3u8\"\n" + - " }\n" + - " }\n" + - " },\n" + - " {\n" + - " \"slug\": \"slides-translated\",\n" + - " \"display\": \"Hall 1 Slides (Translation)\",\n" + - " \"type\": \"slides\",\n" + - " \"isTranslated\": true,\n" + - " \"videoSize\": [\n" + - " 1024,\n" + - " 576\n" + - " ],\n" + - " \"urls\": {\n" + - " \"webm\": {\n" + - " \"display\": \"WebM\",\n" + - " \"tech\": \"1024x576, VP8+Vorbis in WebM, XXX kBit/s\",\n" + - " \"url\": \"http://cdn.c3voc.de/s1_translated_slides.webm\"\n" + - " },\n" + - " \"hls\": {\n" + - " \"display\": \"HLS\",\n" + - " \"tech\": \"1024x576, h264+AAC im MPEG-TS-Container via HTTP, XXX kBit/s\",\n" + - " \"url\": \"http://cdn.c3voc.de/hls/s1_translated_slides.m3u8\"\n" + - " }\n" + - " }\n" + - " },\n" + - " {\n" + - " \"slug\": \"audio-native\",\n" + - " \"display\": \"Hall 1 Audio\",\n" + - " \"type\": \"audio\",\n" + - " \"isTranslated\": false,\n" + - " \"videoSize\": null,\n" + - " \"urls\": {\n" + - " \"mp3\": {\n" + - " \"display\": \"MP3\",\n" + - " \"tech\": \"MP3-Audio, 96 kBit/s\",\n" + - " \"url\": \"http://cdn.c3voc.de/s1_native.mp3\"\n" + - " },\n" + - " \"opus\": {\n" + - " \"display\": \"Opus\",\n" + - " \"tech\": \"Opus-Audio, 64 kBit/s\",\n" + - " \"url\": \"http://cdn.c3voc.de/s1_native.opus\"\n" + - " }\n" + - " }\n" + - " },\n" + - " {\n" + - " \"slug\": \"audio-translated\",\n" + - " \"display\": \"Hall 1 Audio (Translation)\",\n" + - " \"type\": \"audio\",\n" + - " \"isTranslated\": true,\n" + - " \"videoSize\": null,\n" + - " \"urls\": {\n" + - " \"mp3\": {\n" + - " \"display\": \"MP3\",\n" + - " \"tech\": \"MP3-Audio, 96 kBit/s\",\n" + - " \"url\": \"http://cdn.c3voc.de/s1_translated.mp3\"\n" + - " },\n" + - " \"opus\": {\n" + - " \"display\": \"Opus\",\n" + - " \"tech\": \"Opus-Audio, 64 kBit/s\",\n" + - " \"url\": \"http://cdn.c3voc.de/s1_translated.opus\"\n" + - " }\n" + - " }\n" + - " }\n" + - " ]\n" + - " },\n" + - " {\n" + - " \"slug\": \"hall2\",\n" + - " \"schedulename\": \"Hall 2\",\n" + - " \"thumb\": \"https://static.media.ccc.de/media/conferences/archconf/2020/6390-hd.jpg\",\n" + - " \"link\": \"http://localhost:8000/streams/32c3/hall2?forceopen=yess\",\n" + - " \"display\": \"Hall 2\",\n" + - " \"streams\": [\n" + - " {\n" + - " \"slug\": \"hd-native\",\n" + - " \"display\": \"Hall 2 FullHD Video\",\n" + - " \"type\": \"video\",\n" + - " \"isTranslated\": false,\n" + - " \"videoSize\": [\n" + - " 1920,\n" + - " 1080\n" + - " ],\n" + - " \"urls\": {\n" + - " \"webm\": {\n" + - " \"display\": \"WebM\",\n" + - " \"tech\": \"1920x1080, VP8+Vorbis in WebM, 2.8 MBit/s\",\n" + - " \"url\": \"http://cdn.c3voc.de/s2_native_hd.webm\"\n" + - " },\n" + - " \"hls\": {\n" + - " \"display\": \"HLS\",\n" + - " \"tech\": \"1920x1080, h264+AAC im MPEG-TS-Container via HTTP, 3 MBit/s\",\n" + - " \"url\": \"http://cdn.c3voc.de/hls/s2_native_hd.m3u8\"\n" + - " }\n" + - " }\n" + - " },\n" + - " {\n" + - " \"slug\": \"hd-translated\",\n" + - " \"display\": \"Hall 2 FullHD Video (Translation)\",\n" + - " \"type\": \"video\",\n" + - " \"isTranslated\": true,\n" + - " \"videoSize\": [\n" + - " 1920,\n" + - " 1080\n" + - " ],\n" + - " \"urls\": {\n" + - " \"webm\": {\n" + - " \"display\": \"WebM\",\n" + - " \"tech\": \"1920x1080, VP8+Vorbis in WebM, 2.8 MBit/s\",\n" + - " \"url\": \"http://cdn.c3voc.de/s2_translated_hd.webm\"\n" + - " },\n" + - " \"hls\": {\n" + - " \"display\": \"HLS\",\n" + - " \"tech\": \"1920x1080, h264+AAC im MPEG-TS-Container via HTTP, 3 MBit/s\",\n" + - " \"url\": \"http://cdn.c3voc.de/hls/s2_translated_hd.m3u8\"\n" + - " }\n" + - " }\n" + - " },\n" + - " {\n" + - " \"slug\": \"sd-native\",\n" + - " \"display\": \"Hall 2 SD Video\",\n" + - " \"type\": \"video\",\n" + - " \"isTranslated\": false,\n" + - " \"videoSize\": [\n" + - " 1024,\n" + - " 576\n" + - " ],\n" + - " \"urls\": {\n" + - " \"webm\": {\n" + - " \"display\": \"WebM\",\n" + - " \"tech\": \"1024x576, VP8+Vorbis in WebM, 800 kBit/s\",\n" + - " \"url\": \"http://cdn.c3voc.de/s2_native_sd.webm\"\n" + - " },\n" + - " \"hls\": {\n" + - " \"display\": \"HLS\",\n" + - " \"tech\": \"1024x576, h264+AAC im MPEG-TS-Container via HTTP, 800 kBit/s\",\n" + - " \"url\": \"http://cdn.c3voc.de/hls/s2_native_sd.m3u8\"\n" + - " }\n" + - " }\n" + - " },\n" + - " {\n" + - " \"slug\": \"sd-translated\",\n" + - " \"display\": \"Hall 2 SD Video (Translation)\",\n" + - " \"type\": \"video\",\n" + - " \"isTranslated\": true,\n" + - " \"videoSize\": [\n" + - " 1024,\n" + - " 576\n" + - " ],\n" + - " \"urls\": {\n" + - " \"webm\": {\n" + - " \"display\": \"WebM\",\n" + - " \"tech\": \"1024x576, VP8+Vorbis in WebM, 800 kBit/s\",\n" + - " \"url\": \"http://cdn.c3voc.de/s2_translated_sd.webm\"\n" + - " },\n" + - " \"hls\": {\n" + - " \"display\": \"HLS\",\n" + - " \"tech\": \"1024x576, h264+AAC im MPEG-TS-Container via HTTP, 800 kBit/s\",\n" + - " \"url\": \"http://cdn.c3voc.de/hls/s2_translated_sd.m3u8\"\n" + - " }\n" + - " }\n" + - " },\n" + - " {\n" + - " \"slug\": \"slides-native\",\n" + - " \"display\": \"Hall 2 Slides\",\n" + - " \"type\": \"slides\",\n" + - " \"isTranslated\": false,\n" + - " \"videoSize\": [\n" + - " 1024,\n" + - " 576\n" + - " ],\n" + - " \"urls\": {\n" + - " \"webm\": {\n" + - " \"display\": \"WebM\",\n" + - " \"tech\": \"1024x576, VP8+Vorbis in WebM, XXX kBit/s\",\n" + - " \"url\": \"http://cdn.c3voc.de/s2_native_slides.webm\"\n" + - " },\n" + - " \"hls\": {\n" + - " \"display\": \"HLS\",\n" + - " \"tech\": \"1024x576, h264+AAC im MPEG-TS-Container via HTTP, XXX kBit/s\",\n" + - " \"url\": \"http://cdn.c3voc.de/hls/s2_native_slides.m3u8\"\n" + - " }\n" + - " }\n" + - " },\n" + - " {\n" + - " \"slug\": \"slides-translated\",\n" + - " \"display\": \"Hall 2 Slides (Translation)\",\n" + - " \"type\": \"slides\",\n" + - " \"isTranslated\": true,\n" + - " \"videoSize\": [\n" + - " 1024,\n" + - " 576\n" + - " ],\n" + - " \"urls\": {\n" + - " \"webm\": {\n" + - " \"display\": \"WebM\",\n" + - " \"tech\": \"1024x576, VP8+Vorbis in WebM, XXX kBit/s\",\n" + - " \"url\": \"http://cdn.c3voc.de/s2_translated_slides.webm\"\n" + - " },\n" + - " \"hls\": {\n" + - " \"display\": \"HLS\",\n" + - " \"tech\": \"1024x576, h264+AAC im MPEG-TS-Container via HTTP, XXX kBit/s\",\n" + - " \"url\": \"http://cdn.c3voc.de/hls/s2_translated_slides.m3u8\"\n" + - " }\n" + - " }\n" + - " },\n" + - " {\n" + - " \"slug\": \"audio-native\",\n" + - " \"display\": \"Hall 2 Audio\",\n" + - " \"type\": \"audio\",\n" + - " \"isTranslated\": false,\n" + - " \"videoSize\": null,\n" + - " \"urls\": {\n" + - " \"mp3\": {\n" + - " \"display\": \"MP3\",\n" + - " \"tech\": \"MP3-Audio, 96 kBit/s\",\n" + - " \"url\": \"http://cdn.c3voc.de/s2_native.mp3\"\n" + - " },\n" + - " \"opus\": {\n" + - " \"display\": \"Opus\",\n" + - " \"tech\": \"Opus-Audio, 64 kBit/s\",\n" + - " \"url\": \"http://cdn.c3voc.de/s2_native.opus\"\n" + - " }\n" + - " }\n" + - " },\n" + - " {\n" + - " \"slug\": \"audio-translated\",\n" + - " \"display\": \"Hall 2 Audio (Translation)\",\n" + - " \"type\": \"audio\",\n" + - " \"isTranslated\": true,\n" + - " \"videoSize\": null,\n" + - " \"urls\": {\n" + - " \"mp3\": {\n" + - " \"display\": \"MP3\",\n" + - " \"tech\": \"MP3-Audio, 96 kBit/s\",\n" + - " \"url\": \"http://cdn.c3voc.de/s2_translated.mp3\"\n" + - " },\n" + - " \"opus\": {\n" + - " \"display\": \"Opus\",\n" + - " \"tech\": \"Opus-Audio, 64 kBit/s\",\n" + - " \"url\": \"http://cdn.c3voc.de/s2_translated.opus\"\n" + - " }\n" + - " }\n" + - " }\n" + - " ]\n" + - " },\n" + - " {\n" + - " \"slug\": \"hallg\",\n" + - " \"schedulename\": \"Hall G\",\n" + - " \"thumb\": \"https://static.media.ccc.de/media/events/privacyweek/2020/368-hd.jpg\",\n" + - " \"link\": \"http://localhost:8000/streams/32c3/hallg?forceopen=yess\",\n" + - " \"display\": \"Hall G\",\n" + - " \"streams\": [\n" + - " {\n" + - " \"slug\": \"hd-native\",\n" + - " \"display\": \"Hall G FullHD Video\",\n" + - " \"type\": \"video\",\n" + - " \"isTranslated\": false,\n" + - " \"videoSize\": [\n" + - " 1920,\n" + - " 1080\n" + - " ],\n" + - " \"urls\": {\n" + - " \"webm\": {\n" + - " \"display\": \"WebM\",\n" + - " \"tech\": \"1920x1080, VP8+Vorbis in WebM, 2.8 MBit/s\",\n" + - " \"url\": \"http://cdn.c3voc.de/s3_native_hd.webm\"\n" + - " },\n" + - " \"hls\": {\n" + - " \"display\": \"HLS\",\n" + - " \"tech\": \"1920x1080, h264+AAC im MPEG-TS-Container via HTTP, 3 MBit/s\",\n" + - " \"url\": \"http://cdn.c3voc.de/hls/s3_native_hd.m3u8\"\n" + - " }\n" + - " }\n" + - " },\n" + - " {\n" + - " \"slug\": \"hd-translated\",\n" + - " \"display\": \"Hall G FullHD Video (Translation)\",\n" + - " \"type\": \"video\",\n" + - " \"isTranslated\": true,\n" + - " \"videoSize\": [\n" + - " 1920,\n" + - " 1080\n" + - " ],\n" + - " \"urls\": {\n" + - " \"webm\": {\n" + - " \"display\": \"WebM\",\n" + - " \"tech\": \"1920x1080, VP8+Vorbis in WebM, 2.8 MBit/s\",\n" + - " \"url\": \"http://cdn.c3voc.de/s3_translated_hd.webm\"\n" + - " },\n" + - " \"hls\": {\n" + - " \"display\": \"HLS\",\n" + - " \"tech\": \"1920x1080, h264+AAC im MPEG-TS-Container via HTTP, 3 MBit/s\",\n" + - " \"url\": \"http://cdn.c3voc.de/hls/s3_translated_hd.m3u8\"\n" + - " }\n" + - " }\n" + - " },\n" + - " {\n" + - " \"slug\": \"sd-native\",\n" + - " \"display\": \"Hall G SD Video\",\n" + - " \"type\": \"video\",\n" + - " \"isTranslated\": false,\n" + - " \"videoSize\": [\n" + - " 1024,\n" + - " 576\n" + - " ],\n" + - " \"urls\": {\n" + - " \"webm\": {\n" + - " \"display\": \"WebM\",\n" + - " \"tech\": \"1024x576, VP8+Vorbis in WebM, 800 kBit/s\",\n" + - " \"url\": \"http://cdn.c3voc.de/s3_native_sd.webm\"\n" + - " },\n" + - " \"hls\": {\n" + - " \"display\": \"HLS\",\n" + - " \"tech\": \"1024x576, h264+AAC im MPEG-TS-Container via HTTP, 800 kBit/s\",\n" + - " \"url\": \"http://cdn.c3voc.de/hls/s3_native_sd.m3u8\"\n" + - " }\n" + - " }\n" + - " },\n" + - " {\n" + - " \"slug\": \"sd-translated\",\n" + - " \"display\": \"Hall G SD Video (Translation)\",\n" + - " \"type\": \"video\",\n" + - " \"isTranslated\": true,\n" + - " \"videoSize\": [\n" + - " 1024,\n" + - " 576\n" + - " ],\n" + - " \"urls\": {\n" + - " \"webm\": {\n" + - " \"display\": \"WebM\",\n" + - " \"tech\": \"1024x576, VP8+Vorbis in WebM, 800 kBit/s\",\n" + - " \"url\": \"http://cdn.c3voc.de/s3_translated_sd.webm\"\n" + - " },\n" + - " \"hls\": {\n" + - " \"display\": \"HLS\",\n" + - " \"tech\": \"1024x576, h264+AAC im MPEG-TS-Container via HTTP, 800 kBit/s\",\n" + - " \"url\": \"http://cdn.c3voc.de/hls/s3_translated_sd.m3u8\"\n" + - " }\n" + - " }\n" + - " },\n" + - " {\n" + - " \"slug\": \"slides-native\",\n" + - " \"display\": \"Hall G Slides\",\n" + - " \"type\": \"slides\",\n" + - " \"isTranslated\": false,\n" + - " \"videoSize\": [\n" + - " 1024,\n" + - " 576\n" + - " ],\n" + - " \"urls\": {\n" + - " \"webm\": {\n" + - " \"display\": \"WebM\",\n" + - " \"tech\": \"1024x576, VP8+Vorbis in WebM, XXX kBit/s\",\n" + - " \"url\": \"http://cdn.c3voc.de/s3_native_slides.webm\"\n" + - " },\n" + - " \"hls\": {\n" + - " \"display\": \"HLS\",\n" + - " \"tech\": \"1024x576, h264+AAC im MPEG-TS-Container via HTTP, XXX kBit/s\",\n" + - " \"url\": \"http://cdn.c3voc.de/hls/s3_native_slides.m3u8\"\n" + - " }\n" + - " }\n" + - " },\n" + - " {\n" + - " \"slug\": \"slides-translated\",\n" + - " \"display\": \"Hall G Slides (Translation)\",\n" + - " \"type\": \"slides\",\n" + - " \"isTranslated\": true,\n" + - " \"videoSize\": [\n" + - " 1024,\n" + - " 576\n" + - " ],\n" + - " \"urls\": {\n" + - " \"webm\": {\n" + - " \"display\": \"WebM\",\n" + - " \"tech\": \"1024x576, VP8+Vorbis in WebM, XXX kBit/s\",\n" + - " \"url\": \"http://cdn.c3voc.de/s3_translated_slides.webm\"\n" + - " },\n" + - " \"hls\": {\n" + - " \"display\": \"HLS\",\n" + - " \"tech\": \"1024x576, h264+AAC im MPEG-TS-Container via HTTP, XXX kBit/s\",\n" + - " \"url\": \"http://cdn.c3voc.de/hls/s3_translated_slides.m3u8\"\n" + - " }\n" + - " }\n" + - " },\n" + - " {\n" + - " \"slug\": \"audio-native\",\n" + - " \"display\": \"Hall G Audio\",\n" + - " \"type\": \"audio\",\n" + - " \"isTranslated\": false,\n" + - " \"videoSize\": null,\n" + - " \"urls\": {\n" + - " \"mp3\": {\n" + - " \"display\": \"MP3\",\n" + - " \"tech\": \"MP3-Audio, 96 kBit/s\",\n" + - " \"url\": \"http://cdn.c3voc.de/s3_native.mp3\"\n" + - " },\n" + - " \"opus\": {\n" + - " \"display\": \"Opus\",\n" + - " \"tech\": \"Opus-Audio, 64 kBit/s\",\n" + - " \"url\": \"http://cdn.c3voc.de/s3_native.opus\"\n" + - " }\n" + - " }\n" + - " },\n" + - " {\n" + - " \"slug\": \"audio-translated\",\n" + - " \"display\": \"Hall G Audio (Translation)\",\n" + - " \"type\": \"audio\",\n" + - " \"isTranslated\": true,\n" + - " \"videoSize\": null,\n" + - " \"urls\": {\n" + - " \"mp3\": {\n" + - " \"display\": \"MP3\",\n" + - " \"tech\": \"MP3-Audio, 96 kBit/s\",\n" + - " \"url\": \"http://cdn.c3voc.de/s3_translated.mp3\"\n" + - " },\n" + - " \"opus\": {\n" + - " \"display\": \"Opus\",\n" + - " \"tech\": \"Opus-Audio, 64 kBit/s\",\n" + - " \"url\": \"http://cdn.c3voc.de/s3_translated.opus\"\n" + - " }\n" + - " }\n" + - " }\n" + - " ]\n" + - " },\n" + - " {\n" + - " \"slug\": \"hall6\",\n" + - " \"schedulename\": \"Hall 6\",\n" + - " \"thumb\": \"https://static.media.ccc.de/media/conferences/archconf/2020/6307-hd.jpg\",\n" + - " \"link\": \"http://localhost:8000/streams/32c3/hall6?forceopen=yess\",\n" + - " \"display\": \"Hall 6\",\n" + - " \"streams\": [\n" + - " {\n" + - " \"slug\": \"hd-native\",\n" + - " \"display\": \"Hall 6 FullHD Video\",\n" + - " \"type\": \"video\",\n" + - " \"isTranslated\": false,\n" + - " \"videoSize\": [\n" + - " 1920,\n" + - " 1080\n" + - " ],\n" + - " \"urls\": {\n" + - " \"webm\": {\n" + - " \"display\": \"WebM\",\n" + - " \"tech\": \"1920x1080, VP8+Vorbis in WebM, 2.8 MBit/s\",\n" + - " \"url\": \"http://cdn.c3voc.de/s4_native_hd.webm\"\n" + - " },\n" + - " \"hls\": {\n" + - " \"display\": \"HLS\",\n" + - " \"tech\": \"1920x1080, h264+AAC im MPEG-TS-Container via HTTP, 3 MBit/s\",\n" + - " \"url\": \"http://cdn.c3voc.de/hls/s4_native_hd.m3u8\"\n" + - " }\n" + - " }\n" + - " },\n" + - " {\n" + - " \"slug\": \"hd-translated\",\n" + - " \"display\": \"Hall 6 FullHD Video (Translation)\",\n" + - " \"type\": \"video\",\n" + - " \"isTranslated\": true,\n" + - " \"videoSize\": [\n" + - " 1920,\n" + - " 1080\n" + - " ],\n" + - " \"urls\": {\n" + - " \"webm\": {\n" + - " \"display\": \"WebM\",\n" + - " \"tech\": \"1920x1080, VP8+Vorbis in WebM, 2.8 MBit/s\",\n" + - " \"url\": \"http://cdn.c3voc.de/s4_translated_hd.webm\"\n" + - " },\n" + - " \"hls\": {\n" + - " \"display\": \"HLS\",\n" + - " \"tech\": \"1920x1080, h264+AAC im MPEG-TS-Container via HTTP, 3 MBit/s\",\n" + - " \"url\": \"http://cdn.c3voc.de/hls/s4_translated_hd.m3u8\"\n" + - " }\n" + - " }\n" + - " },\n" + - " {\n" + - " \"slug\": \"sd-native\",\n" + - " \"display\": \"Hall 6 SD Video\",\n" + - " \"type\": \"video\",\n" + - " \"isTranslated\": false,\n" + - " \"videoSize\": [\n" + - " 1024,\n" + - " 576\n" + - " ],\n" + - " \"urls\": {\n" + - " \"webm\": {\n" + - " \"display\": \"WebM\",\n" + - " \"tech\": \"1024x576, VP8+Vorbis in WebM, 800 kBit/s\",\n" + - " \"url\": \"http://cdn.c3voc.de/s4_native_sd.webm\"\n" + - " },\n" + - " \"hls\": {\n" + - " \"display\": \"HLS\",\n" + - " \"tech\": \"1024x576, h264+AAC im MPEG-TS-Container via HTTP, 800 kBit/s\",\n" + - " \"url\": \"http://cdn.c3voc.de/hls/s4_native_sd.m3u8\"\n" + - " }\n" + - " }\n" + - " },\n" + - " {\n" + - " \"slug\": \"sd-translated\",\n" + - " \"display\": \"Hall 6 SD Video (Translation)\",\n" + - " \"type\": \"video\",\n" + - " \"isTranslated\": true,\n" + - " \"videoSize\": [\n" + - " 1024,\n" + - " 576\n" + - " ],\n" + - " \"urls\": {\n" + - " \"webm\": {\n" + - " \"display\": \"WebM\",\n" + - " \"tech\": \"1024x576, VP8+Vorbis in WebM, 800 kBit/s\",\n" + - " \"url\": \"http://cdn.c3voc.de/s4_translated_sd.webm\"\n" + - " },\n" + - " \"hls\": {\n" + - " \"display\": \"HLS\",\n" + - " \"tech\": \"1024x576, h264+AAC im MPEG-TS-Container via HTTP, 800 kBit/s\",\n" + - " \"url\": \"http://cdn.c3voc.de/hls/s4_translated_sd.m3u8\"\n" + - " }\n" + - " }\n" + - " },\n" + - " {\n" + - " \"slug\": \"slides-native\",\n" + - " \"display\": \"Hall 6 Slides\",\n" + - " \"type\": \"slides\",\n" + - " \"isTranslated\": false,\n" + - " \"videoSize\": [\n" + - " 1024,\n" + - " 576\n" + - " ],\n" + - " \"urls\": {\n" + - " \"webm\": {\n" + - " \"display\": \"WebM\",\n" + - " \"tech\": \"1024x576, VP8+Vorbis in WebM, XXX kBit/s\",\n" + - " \"url\": \"http://cdn.c3voc.de/s4_native_slides.webm\"\n" + - " },\n" + - " \"hls\": {\n" + - " \"display\": \"HLS\",\n" + - " \"tech\": \"1024x576, h264+AAC im MPEG-TS-Container via HTTP, XXX kBit/s\",\n" + - " \"url\": \"http://cdn.c3voc.de/hls/s4_native_slides.m3u8\"\n" + - " }\n" + - " }\n" + - " },\n" + - " {\n" + - " \"slug\": \"slides-translated\",\n" + - " \"display\": \"Hall 6 Slides (Translation)\",\n" + - " \"type\": \"slides\",\n" + - " \"isTranslated\": true,\n" + - " \"videoSize\": [\n" + - " 1024,\n" + - " 576\n" + - " ],\n" + - " \"urls\": {\n" + - " \"webm\": {\n" + - " \"display\": \"WebM\",\n" + - " \"tech\": \"1024x576, VP8+Vorbis in WebM, XXX kBit/s\",\n" + - " \"url\": \"http://cdn.c3voc.de/s4_translated_slides.webm\"\n" + - " },\n" + - " \"hls\": {\n" + - " \"display\": \"HLS\",\n" + - " \"tech\": \"1024x576, h264+AAC im MPEG-TS-Container via HTTP, XXX kBit/s\",\n" + - " \"url\": \"http://cdn.c3voc.de/hls/s4_translated_slides.m3u8\"\n" + - " }\n" + - " }\n" + - " },\n" + - " {\n" + - " \"slug\": \"audio-native\",\n" + - " \"display\": \"Hall 6 Audio\",\n" + - " \"type\": \"audio\",\n" + - " \"isTranslated\": false,\n" + - " \"videoSize\": null,\n" + - " \"urls\": {\n" + - " \"mp3\": {\n" + - " \"display\": \"MP3\",\n" + - " \"tech\": \"MP3-Audio, 96 kBit/s\",\n" + - " \"url\": \"http://cdn.c3voc.de/s4_native.mp3\"\n" + - " },\n" + - " \"opus\": {\n" + - " \"display\": \"Opus\",\n" + - " \"tech\": \"Opus-Audio, 64 kBit/s\",\n" + - " \"url\": \"http://cdn.c3voc.de/s4_native.opus\"\n" + - " }\n" + - " }\n" + - " },\n" + - " {\n" + - " \"slug\": \"audio-translated\",\n" + - " \"display\": \"Hall 6 Audio (Translation)\",\n" + - " \"type\": \"audio\",\n" + - " \"isTranslated\": true,\n" + - " \"videoSize\": null,\n" + - " \"urls\": {\n" + - " \"mp3\": {\n" + - " \"display\": \"MP3\",\n" + - " \"tech\": \"MP3-Audio, 96 kBit/s\",\n" + - " \"url\": \"http://cdn.c3voc.de/s4_translated.mp3\"\n" + - " },\n" + - " \"opus\": {\n" + - " \"display\": \"Opus\",\n" + - " \"tech\": \"Opus-Audio, 64 kBit/s\",\n" + - " \"url\": \"http://cdn.c3voc.de/s4_translated.opus\"\n" + - " }\n" + - " }\n" + - " }\n" + - " ]\n" + - " }\n" + - " ]\n" + - " },\n" + - " {\n" + - " \"group\": \"Live Music\",\n" + - " \"rooms\": [\n" + - " {\n" + - " \"slug\": \"lounge\",\n" + - " \"schedulename\": \"Lounge\",\n" + - " \"thumb\": \"https://static.media.ccc.de/media/events/BigBrotherAwards/2020/2-hd.jpg\",\n" + - " \"link\": \"http://localhost:8000/streams/32c3/lounge?forceopen=yess\",\n" + - " \"display\": \"Lounge\",\n" + - " \"streams\": [\n" + - " {\n" + - " \"slug\": \"music-native\",\n" + - " \"display\": \"Lounge Radio\",\n" + - " \"type\": \"music\",\n" + - " \"isTranslated\": false,\n" + - " \"videoSize\": null,\n" + - " \"urls\": {\n" + - " \"mp3\": {\n" + - " \"display\": \"MP3\",\n" + - " \"tech\": \"MP3-Audio, 192 kBit/s\",\n" + - " \"url\": \"http://cdn.c3voc.de/lounge.mp3\"\n" + - " },\n" + - " \"opus\": {\n" + - " \"display\": \"Opus\",\n" + - " \"tech\": \"Opus-Audio, 96 kBit/s\",\n" + - " \"url\": \"http://cdn.c3voc.de/lounge.opus\"\n" + - " }\n" + - " }\n" + - " }\n" + - " ]\n" + - " },\n" + - " {\n" + - " \"slug\": \"ambient\",\n" + - " \"schedulename\": \"Ambient\",\n" + - " \"thumb\": \"https://static.media.ccc.de/media/congress/2019/11175-hd.jpg\",\n" + - " \"link\": \"http://localhost:8000/streams/32c3/ambient?forceopen=yess\",\n" + - " \"display\": \"Ambient\",\n" + - " \"streams\": [\n" + - " {\n" + - " \"slug\": \"music-native\",\n" + - " \"display\": \"Ambient Radio\",\n" + - " \"type\": \"music\",\n" + - " \"isTranslated\": false,\n" + - " \"videoSize\": null,\n" + - " \"urls\": {\n" + - " \"mp3\": {\n" + - " \"display\": \"MP3\",\n" + - " \"tech\": \"MP3-Audio, 192 kBit/s\",\n" + - " \"url\": \"http://cdn.c3voc.de/ambient.mp3\"\n" + - " },\n" + - " \"opus\": {\n" + - " \"display\": \"Opus\",\n" + - " \"tech\": \"Opus-Audio, 96 kBit/s\",\n" + - " \"url\": \"http://cdn.c3voc.de/ambient.opus\"\n" + - " }\n" + - " }\n" + - " }\n" + - " ]\n" + - " },\n" + - " {\n" + - " \"slug\": \"dome\",\n" + - " \"schedulename\": \"Dome\",\n" + - " \"thumb\": \"https://static.media.ccc.de/media/regional/c4/openchaos/2011-hd.jpg\",\n" + - " \"link\": \"http://localhost:8000/streams/32c3/dome?forceopen=yess\",\n" + - " \"display\": \"Dome\",\n" + - " \"streams\": [\n" + - " {\n" + - " \"slug\": \"music-native\",\n" + - " \"display\": \"Dome Radio\",\n" + - " \"type\": \"music\",\n" + - " \"isTranslated\": false,\n" + - " \"videoSize\": null,\n" + - " \"urls\": {\n" + - " \"mp3\": {\n" + - " \"display\": \"MP3\",\n" + - " \"tech\": \"MP3-Audio, 192 kBit/s\",\n" + - " \"url\": \"http://cdn.c3voc.de/dome.mp3\"\n" + - " },\n" + - " \"opus\": {\n" + - " \"display\": \"Opus\",\n" + - " \"tech\": \"Opus-Audio, 96 kBit/s\",\n" + - " \"url\": \"http://cdn.c3voc.de/dome.opus\"\n" + - " }\n" + - " }\n" + - " }\n" + - " ]\n" + - " },\n" + - " {\n" + - " \"slug\": \"chaos-west\",\n" + - " \"schedulename\": \"Chaos-West\",\n" + - " \"thumb\": \"http://localhost:8000/streams/thumbs/chaos-west.png\",\n" + - " \"link\": \"http://localhost:8000/streams/32c3/chaos-west?forceopen=yess\",\n" + - " \"display\": \"Chaos-West\",\n" + - " \"streams\": [\n" + - " {\n" + - " \"slug\": \"music-native\",\n" + - " \"display\": \"Chaos-West Radio\",\n" + - " \"type\": \"music\",\n" + - " \"isTranslated\": false,\n" + - " \"videoSize\": null,\n" + - " \"urls\": {\n" + - " \"mp3\": {\n" + - " \"display\": \"MP3\",\n" + - " \"tech\": \"MP3-Audio, 192 kBit/s\",\n" + - " \"url\": \"http://cdn.c3voc.de/chaos-west.mp3\"\n" + - " },\n" + - " \"opus\": {\n" + - " \"display\": \"Opus\",\n" + - " \"tech\": \"Opus-Audio, 96 kBit/s\",\n" + - " \"url\": \"http://cdn.c3voc.de/chaos-west.opus\"\n" + - " }\n" + - " }\n" + - " }\n" + - " ]\n" + - " },\n" + - " {\n" + - " \"slug\": \"radio\",\n" + - " \"schedulename\": \"Radio Fairydust\",\n" + - " \"thumb\": \"http://localhost:8000/streams/thumbs/radio.png\",\n" + - " \"link\": \"http://localhost:8000/streams/32c3/radio?forceopen=yess\",\n" + - " \"display\": \"Radio Fairydust\",\n" + - " \"streams\": [\n" + - " {\n" + - " \"slug\": \"music-native\",\n" + - " \"display\": \"Radio Fairydust Radio\",\n" + - " \"type\": \"music\",\n" + - " \"isTranslated\": false,\n" + - " \"videoSize\": null,\n" + - " \"urls\": {\n" + - " \"mp3\": {\n" + - " \"display\": \"MP3\",\n" + - " \"tech\": \"MP3-Audio, 192 kBit/s\",\n" + - " \"url\": \"http://cdn.c3voc.de/radio.mp3\"\n" + - " },\n" + - " \"opus\": {\n" + - " \"display\": \"Opus\",\n" + - " \"tech\": \"Opus-Audio, 96 kBit/s\",\n" + - " \"url\": \"http://cdn.c3voc.de/radio.opus\"\n" + - " }\n" + - " }\n" + - " }\n" + - " ]\n" + - " }\n" + - " ]\n" + - " },\n" + - " {\n" + - " \"group\": \"Live Podcasts\",\n" + - " \"rooms\": [\n" + - " {\n" + - " \"slug\": \"sendezentrum\",\n" + - " \"schedulename\": \"Bühne\",\n" + - " \"thumb\": \"http://localhost:8000/streams/thumbs/s5.png\",\n" + - " \"link\": \"http://localhost:8000/streams/32c3/sendezentrum?forceopen=yess\",\n" + - " \"display\": \"Sendezentrum\",\n" + - " \"streams\": [\n" + - " {\n" + - " \"slug\": \"hd-native\",\n" + - " \"display\": \"Sendezentrum FullHD Video\",\n" + - " \"type\": \"video\",\n" + - " \"isTranslated\": false,\n" + - " \"videoSize\": [\n" + - " 1920,\n" + - " 1080\n" + - " ],\n" + - " \"urls\": {\n" + - " \"webm\": {\n" + - " \"display\": \"WebM\",\n" + - " \"tech\": \"1920x1080, VP8+Vorbis in WebM, 2.8 MBit/s\",\n" + - " \"url\": \"http://cdn.c3voc.de/s5_native_hd.webm\"\n" + - " },\n" + - " \"hls\": {\n" + - " \"display\": \"HLS\",\n" + - " \"tech\": \"1920x1080, h264+AAC im MPEG-TS-Container via HTTP, 3 MBit/s\",\n" + - " \"url\": \"http://cdn.c3voc.de/hls/s5_native_hd.m3u8\"\n" + - " }\n" + - " }\n" + - " },\n" + - " {\n" + - " \"slug\": \"sd-native\",\n" + - " \"display\": \"Sendezentrum SD Video\",\n" + - " \"type\": \"video\",\n" + - " \"isTranslated\": false,\n" + - " \"videoSize\": [\n" + - " 1024,\n" + - " 576\n" + - " ],\n" + - " \"urls\": {\n" + - " \"webm\": {\n" + - " \"display\": \"WebM\",\n" + - " \"tech\": \"1024x576, VP8+Vorbis in WebM, 800 kBit/s\",\n" + - " \"url\": \"http://cdn.c3voc.de/s5_native_sd.webm\"\n" + - " },\n" + - " \"hls\": {\n" + - " \"display\": \"HLS\",\n" + - " \"tech\": \"1024x576, h264+AAC im MPEG-TS-Container via HTTP, 800 kBit/s\",\n" + - " \"url\": \"http://cdn.c3voc.de/hls/s5_native_sd.m3u8\"\n" + - " }\n" + - " }\n" + - " },\n" + - " {\n" + - " \"slug\": \"audio-native\",\n" + - " \"display\": \"Sendezentrum Audio\",\n" + - " \"type\": \"audio\",\n" + - " \"isTranslated\": false,\n" + - " \"videoSize\": null,\n" + - " \"urls\": {\n" + - " \"mp3\": {\n" + - " \"display\": \"MP3\",\n" + - " \"tech\": \"MP3-Audio, 96 kBit/s\",\n" + - " \"url\": \"http://cdn.c3voc.de/s5_native.mp3\"\n" + - " },\n" + - " \"opus\": {\n" + - " \"display\": \"Opus\",\n" + - " \"tech\": \"Opus-Audio, 64 kBit/s\",\n" + - " \"url\": \"http://cdn.c3voc.de/s5_native.opus\"\n" + - " }\n" + - " }\n" + - " }\n" + - " ]\n" + - " },\n" + - " {\n" + - " \"slug\": \"podcastertisch\",\n" + - " \"schedulename\": \"Podcaster-Tisch\",\n" + - " \"thumb\": \"http://localhost:8000/streams/thumbs/podcastertisch.png\",\n" + - " \"link\": \"http://localhost:8000/streams/32c3/podcastertisch?forceopen=yess\",\n" + - " \"display\": \"Sendezentrum - Podcastertisch\",\n" + - " \"streams\": [\n" + - " {\n" + - " \"slug\": \"music-native\",\n" + - " \"display\": \"Sendezentrum - Podcastertisch Radio\",\n" + - " \"type\": \"music\",\n" + - " \"isTranslated\": false,\n" + - " \"videoSize\": null,\n" + - " \"urls\": {\n" + - " \"mp3\": {\n" + - " \"display\": \"MP3\",\n" + - " \"tech\": \"MP3-Audio, 192 kBit/s\",\n" + - " \"url\": \"http://cdn.c3voc.de/podcastertisch.mp3\"\n" + - " },\n" + - " \"opus\": {\n" + - " \"display\": \"Opus\",\n" + - " \"tech\": \"Opus-Audio, 96 kBit/s\",\n" + - " \"url\": \"http://cdn.c3voc.de/podcastertisch.opus\"\n" + - " }\n" + - " }\n" + - " }\n" + - " ]\n" + - " }\n" + - " ]\n" + - " }\n" + - " ]\n" + - " }\n" + - "]";*/ - /*site = "[\n" + - " {\n" + - " \"author\": \"CCC\",\n" + - " \"conference\": \"33C3\",\n" + - " \"description\": \"Live-Streaming vom 33C3\",\n" + - " \"endsAt\": \"2016-12-30T20:00:00+0000\",\n" + - " \"groups\": [\n" + - " {\n" + - " \"group\": \"Live\",\n" + - " \"rooms\": [\n" + - " {\n" + - " \"display\": \"Saal 1\",\n" + - " \"link\": \"https://streaming.media.ccc.de/33c3/hall1\",\n" + - " \"schedulename\": \"Saal 1\",\n" + - " \"slug\": \"hall1\",\n" + - " \"streams\": [\n" + - " {\n" + - " \"display\": \"Saal 1 FullHD Video\",\n" + - " \"isTranslated\": false,\n" + - " \"slug\": \"hd-native\",\n" + - " \"type\": \"video\",\n" + - " \"urls\": {\n" + - " \"hls\": {\n" + - " \"display\": \"HLS\",\n" + - " \"tech\": \"1920x1080, h264+AAC im MPEG-TS-Container via HTTP, 3 MBit/s\",\n" + - " \"url\": \"https://cdn.c3voc.de/hls/s1_native_hd.m3u8\"\n" + - " },\n" + - " \"webm\": {\n" + - " \"display\": \"WebM\",\n" + - " \"tech\": \"1920x1080, VP8+Vorbis in WebM, 2.8 MBit/s\",\n" + - " \"url\": \"https://cdn.c3voc.de/s1_native_hd.webm\"\n" + - " }\n" + - " },\n" + - " \"videoSize\": [\n" + - " 1920,\n" + - " 1080\n" + - " ]\n" + - " },\n" + - " {\n" + - " \"display\": \"Saal 1 FullHD Video (Translation)\",\n" + - " \"isTranslated\": true,\n" + - " \"slug\": \"hd-translated\",\n" + - " \"type\": \"video\",\n" + - " \"urls\": {\n" + - " \"hls\": {\n" + - " \"display\": \"HLS\",\n" + - " \"tech\": \"1920x1080, h264+AAC im MPEG-TS-Container via HTTP, 3 MBit/s\",\n" + - " \"url\": \"https://cdn.c3voc.de/hls/s1_translated_hd.m3u8\"\n" + - " },\n" + - " \"webm\": {\n" + - " \"display\": \"WebM\",\n" + - " \"tech\": \"1920x1080, VP8+Vorbis in WebM, 2.8 MBit/s\",\n" + - " \"url\": \"https://cdn.c3voc.de/s1_translated_hd.webm\"\n" + - " }\n" + - " },\n" + - " \"videoSize\": [\n" + - " 1920,\n" + - " 1080\n" + - " ]\n" + - " },\n" + - " {\n" + - " \"display\": \"Saal 1 SD Video\",\n" + - " \"isTranslated\": false,\n" + - " \"slug\": \"sd-native\",\n" + - " \"type\": \"video\",\n" + - " \"urls\": {\n" + - " \"hls\": {\n" + - " \"display\": \"HLS\",\n" + - " \"tech\": \"1024x576, h264+AAC im MPEG-TS-Container via HTTP, 800 kBit/s\",\n" + - " \"url\": \"https://cdn.c3voc.de/hls/s1_native_sd.m3u8\"\n" + - " },\n" + - " \"webm\": {\n" + - " \"display\": \"WebM\",\n" + - " \"tech\": \"1024x576, VP8+Vorbis in WebM, 800 kBit/s\",\n" + - " \"url\": \"https://cdn.c3voc.de/s1_native_sd.webm\"\n" + - " }\n" + - " },\n" + - " \"videoSize\": [\n" + - " 1024,\n" + - " 576\n" + - " ]\n" + - " },\n" + - " {\n" + - " \"display\": \"Saal 1 SD Video (Translation)\",\n" + - " \"isTranslated\": true,\n" + - " \"slug\": \"sd-translated\",\n" + - " \"type\": \"video\",\n" + - " \"urls\": {\n" + - " \"hls\": {\n" + - " \"display\": \"HLS\",\n" + - " \"tech\": \"1024x576, h264+AAC im MPEG-TS-Container via HTTP, 800 kBit/s\",\n" + - " \"url\": \"https://cdn.c3voc.de/hls/s1_translated_sd.m3u8\"\n" + - " },\n" + - " \"webm\": {\n" + - " \"display\": \"WebM\",\n" + - " \"tech\": \"1024x576, VP8+Vorbis in WebM, 800 kBit/s\",\n" + - " \"url\": \"https://cdn.c3voc.de/s1_translated_sd.webm\"\n" + - " }\n" + - " },\n" + - " \"videoSize\": [\n" + - " 1024,\n" + - " 576\n" + - " ]\n" + - " },\n" + - " {\n" + - " \"display\": \"Saal 1 Slides\",\n" + - " \"isTranslated\": false,\n" + - " \"slug\": \"slides-native\",\n" + - " \"type\": \"slides\",\n" + - " \"urls\": {\n" + - " \"hls\": {\n" + - " \"display\": \"HLS\",\n" + - " \"tech\": \"1024x576, h264+AAC im MPEG-TS-Container via HTTP, XXX kBit/s\",\n" + - " \"url\": \"https://cdn.c3voc.de/hls/s1_native_slides.m3u8\"\n" + - " },\n" + - " \"webm\": {\n" + - " \"display\": \"WebM\",\n" + - " \"tech\": \"1024x576, VP8+Vorbis in WebM, XXX kBit/s\",\n" + - " \"url\": \"https://cdn.c3voc.de/s1_native_slides.webm\"\n" + - " }\n" + - " },\n" + - " \"videoSize\": [\n" + - " 1024,\n" + - " 576\n" + - " ]\n" + - " },\n" + - " {\n" + - " \"display\": \"Saal 1 Slides (Translation)\",\n" + - " \"isTranslated\": true,\n" + - " \"slug\": \"slides-translated\",\n" + - " \"type\": \"slides\",\n" + - " \"urls\": {\n" + - " \"hls\": {\n" + - " \"display\": \"HLS\",\n" + - " \"tech\": \"1024x576, h264+AAC im MPEG-TS-Container via HTTP, XXX kBit/s\",\n" + - " \"url\": \"https://cdn.c3voc.de/hls/s1_translated_slides.m3u8\"\n" + - " },\n" + - " \"webm\": {\n" + - " \"display\": \"WebM\",\n" + - " \"tech\": \"1024x576, VP8+Vorbis in WebM, XXX kBit/s\",\n" + - " \"url\": \"https://cdn.c3voc.de/s1_translated_slides.webm\"\n" + - " }\n" + - " },\n" + - " \"videoSize\": [\n" + - " 1024,\n" + - " 576\n" + - " ]\n" + - " },\n" + - " {\n" + - " \"display\": \"Saal 1 Audio\",\n" + - " \"isTranslated\": false,\n" + - " \"slug\": \"audio-native\",\n" + - " \"type\": \"audio\",\n" + - " \"urls\": {\n" + - " \"mp3\": {\n" + - " \"display\": \"MP3\",\n" + - " \"tech\": \"MP3-Audio, 96 kBit/s\",\n" + - " \"url\": \"https://cdn.c3voc.de/s1_native.mp3\"\n" + - " },\n" + - " \"opus\": {\n" + - " \"display\": \"Opus\",\n" + - " \"tech\": \"Opus-Audio, 64 kBit/s\",\n" + - " \"url\": \"https://cdn.c3voc.de/s1_native.opus\"\n" + - " }\n" + - " },\n" + - " \"videoSize\": null\n" + - " },\n" + - " {\n" + - " \"display\": \"Saal 1 Audio (Translation)\",\n" + - " \"isTranslated\": true,\n" + - " \"slug\": \"audio-translated\",\n" + - " \"type\": \"audio\",\n" + - " \"urls\": {\n" + - " \"mp3\": {\n" + - " \"display\": \"MP3\",\n" + - " \"tech\": \"MP3-Audio, 96 kBit/s\",\n" + - " \"url\": \"https://cdn.c3voc.de/s1_translated.mp3\"\n" + - " },\n" + - " \"opus\": {\n" + - " \"display\": \"Opus\",\n" + - " \"tech\": \"Opus-Audio, 64 kBit/s\",\n" + - " \"url\": \"https://cdn.c3voc.de/s1_translated.opus\"\n" + - " }\n" + - " },\n" + - " \"videoSize\": null\n" + - " }\n" + - " ],\n" + - " \"thumb\": \"https://streaming.media.ccc.de/thumbs/s1.png\"\n" + - " },\n" + - " {\n" + - " \"display\": \"Saal 2\",\n" + - " \"link\": \"https://streaming.media.ccc.de/33c3/hall2\",\n" + - " \"schedulename\": \"Saal 2\",\n" + - " \"slug\": \"hall2\",\n" + - " \"streams\": [\n" + - " {\n" + - " \"display\": \"Saal 2 FullHD Video\",\n" + - " \"isTranslated\": false,\n" + - " \"slug\": \"hd-native\",\n" + - " \"type\": \"video\",\n" + - " \"urls\": {\n" + - " \"hls\": {\n" + - " \"display\": \"HLS\",\n" + - " \"tech\": \"1920x1080, h264+AAC im MPEG-TS-Container via HTTP, 3 MBit/s\",\n" + - " \"url\": \"https://cdn.c3voc.de/hls/s2_native_hd.m3u8\"\n" + - " },\n" + - " \"webm\": {\n" + - " \"display\": \"WebM\",\n" + - " \"tech\": \"1920x1080, VP8+Vorbis in WebM, 2.8 MBit/s\",\n" + - " \"url\": \"https://cdn.c3voc.de/s2_native_hd.webm\"\n" + - " }\n" + - " },\n" + - " \"videoSize\": [\n" + - " 1920,\n" + - " 1080\n" + - " ]\n" + - " },\n" + - " {\n" + - " \"display\": \"Saal 2 FullHD Video (Translation)\",\n" + - " \"isTranslated\": true,\n" + - " \"slug\": \"hd-translated\",\n" + - " \"type\": \"video\",\n" + - " \"urls\": {\n" + - " \"hls\": {\n" + - " \"display\": \"HLS\",\n" + - " \"tech\": \"1920x1080, h264+AAC im MPEG-TS-Container via HTTP, 3 MBit/s\",\n" + - " \"url\": \"https://cdn.c3voc.de/hls/s2_translated_hd.m3u8\"\n" + - " },\n" + - " \"webm\": {\n" + - " \"display\": \"WebM\",\n" + - " \"tech\": \"1920x1080, VP8+Vorbis in WebM, 2.8 MBit/s\",\n" + - " \"url\": \"https://cdn.c3voc.de/s2_translated_hd.webm\"\n" + - " }\n" + - " },\n" + - " \"videoSize\": [\n" + - " 1920,\n" + - " 1080\n" + - " ]\n" + - " },\n" + - " {\n" + - " \"display\": \"Saal 2 SD Video\",\n" + - " \"isTranslated\": false,\n" + - " \"slug\": \"sd-native\",\n" + - " \"type\": \"video\",\n" + - " \"urls\": {\n" + - " \"hls\": {\n" + - " \"display\": \"HLS\",\n" + - " \"tech\": \"1024x576, h264+AAC im MPEG-TS-Container via HTTP, 800 kBit/s\",\n" + - " \"url\": \"https://cdn.c3voc.de/hls/s2_native_sd.m3u8\"\n" + - " },\n" + - " \"webm\": {\n" + - " \"display\": \"WebM\",\n" + - " \"tech\": \"1024x576, VP8+Vorbis in WebM, 800 kBit/s\",\n" + - " \"url\": \"https://cdn.c3voc.de/s2_native_sd.webm\"\n" + - " }\n" + - " },\n" + - " \"videoSize\": [\n" + - " 1024,\n" + - " 576\n" + - " ]\n" + - " },\n" + - " {\n" + - " \"display\": \"Saal 2 SD Video (Translation)\",\n" + - " \"isTranslated\": true,\n" + - " \"slug\": \"sd-translated\",\n" + - " \"type\": \"video\",\n" + - " \"urls\": {\n" + - " \"hls\": {\n" + - " \"display\": \"HLS\",\n" + - " \"tech\": \"1024x576, h264+AAC im MPEG-TS-Container via HTTP, 800 kBit/s\",\n" + - " \"url\": \"https://cdn.c3voc.de/hls/s2_translated_sd.m3u8\"\n" + - " },\n" + - " \"webm\": {\n" + - " \"display\": \"WebM\",\n" + - " \"tech\": \"1024x576, VP8+Vorbis in WebM, 800 kBit/s\",\n" + - " \"url\": \"https://cdn.c3voc.de/s2_translated_sd.webm\"\n" + - " }\n" + - " },\n" + - " \"videoSize\": [\n" + - " 1024,\n" + - " 576\n" + - " ]\n" + - " },\n" + - " {\n" + - " \"display\": \"Saal 2 Slides\",\n" + - " \"isTranslated\": false,\n" + - " \"slug\": \"slides-native\",\n" + - " \"type\": \"slides\",\n" + - " \"urls\": {\n" + - " \"hls\": {\n" + - " \"display\": \"HLS\",\n" + - " \"tech\": \"1024x576, h264+AAC im MPEG-TS-Container via HTTP, XXX kBit/s\",\n" + - " \"url\": \"https://cdn.c3voc.de/hls/s2_native_slides.m3u8\"\n" + - " },\n" + - " \"webm\": {\n" + - " \"display\": \"WebM\",\n" + - " \"tech\": \"1024x576, VP8+Vorbis in WebM, XXX kBit/s\",\n" + - " \"url\": \"https://cdn.c3voc.de/s2_native_slides.webm\"\n" + - " }\n" + - " },\n" + - " \"videoSize\": [\n" + - " 1024,\n" + - " 576\n" + - " ]\n" + - " },\n" + - " {\n" + - " \"display\": \"Saal 2 Slides (Translation)\",\n" + - " \"isTranslated\": true,\n" + - " \"slug\": \"slides-translated\",\n" + - " \"type\": \"slides\",\n" + - " \"urls\": {\n" + - " \"hls\": {\n" + - " \"display\": \"HLS\",\n" + - " \"tech\": \"1024x576, h264+AAC im MPEG-TS-Container via HTTP, XXX kBit/s\",\n" + - " \"url\": \"https://cdn.c3voc.de/hls/s2_translated_slides.m3u8\"\n" + - " },\n" + - " \"webm\": {\n" + - " \"display\": \"WebM\",\n" + - " \"tech\": \"1024x576, VP8+Vorbis in WebM, XXX kBit/s\",\n" + - " \"url\": \"https://cdn.c3voc.de/s2_translated_slides.webm\"\n" + - " }\n" + - " },\n" + - " \"videoSize\": [\n" + - " 1024,\n" + - " 576\n" + - " ]\n" + - " },\n" + - " {\n" + - " \"display\": \"Saal 2 Audio\",\n" + - " \"isTranslated\": false,\n" + - " \"slug\": \"audio-native\",\n" + - " \"type\": \"audio\",\n" + - " \"urls\": {\n" + - " \"mp3\": {\n" + - " \"display\": \"MP3\",\n" + - " \"tech\": \"MP3-Audio, 96 kBit/s\",\n" + - " \"url\": \"https://cdn.c3voc.de/s2_native.mp3\"\n" + - " },\n" + - " \"opus\": {\n" + - " \"display\": \"Opus\",\n" + - " \"tech\": \"Opus-Audio, 64 kBit/s\",\n" + - " \"url\": \"https://cdn.c3voc.de/s2_native.opus\"\n" + - " }\n" + - " },\n" + - " \"videoSize\": null\n" + - " },\n" + - " {\n" + - " \"display\": \"Saal 2 Audio (Translation)\",\n" + - " \"isTranslated\": true,\n" + - " \"slug\": \"audio-translated\",\n" + - " \"type\": \"audio\",\n" + - " \"urls\": {\n" + - " \"mp3\": {\n" + - " \"display\": \"MP3\",\n" + - " \"tech\": \"MP3-Audio, 96 kBit/s\",\n" + - " \"url\": \"https://cdn.c3voc.de/s2_translated.mp3\"\n" + - " },\n" + - " \"opus\": {\n" + - " \"display\": \"Opus\",\n" + - " \"tech\": \"Opus-Audio, 64 kBit/s\",\n" + - " \"url\": \"https://cdn.c3voc.de/s2_translated.opus\"\n" + - " }\n" + - " },\n" + - " \"videoSize\": null\n" + - " }\n" + - " ],\n" + - " \"thumb\": \"https://streaming.media.ccc.de/thumbs/s2.png\"\n" + - " },\n" + - " {\n" + - " \"display\": \"Saal G\",\n" + - " \"link\": \"https://streaming.media.ccc.de/33c3/hallg\",\n" + - " \"schedulename\": \"Saal G\",\n" + - " \"slug\": \"hallg\",\n" + - " \"streams\": [\n" + - " {\n" + - " \"display\": \"Saal G FullHD Video\",\n" + - " \"isTranslated\": false,\n" + - " \"slug\": \"hd-native\",\n" + - " \"type\": \"video\",\n" + - " \"urls\": {\n" + - " \"hls\": {\n" + - " \"display\": \"HLS\",\n" + - " \"tech\": \"1920x1080, h264+AAC im MPEG-TS-Container via HTTP, 3 MBit/s\",\n" + - " \"url\": \"https://cdn.c3voc.de/hls/s3_native_hd.m3u8\"\n" + - " },\n" + - " \"webm\": {\n" + - " \"display\": \"WebM\",\n" + - " \"tech\": \"1920x1080, VP8+Vorbis in WebM, 2.8 MBit/s\",\n" + - " \"url\": \"https://cdn.c3voc.de/s3_native_hd.webm\"\n" + - " }\n" + - " },\n" + - " \"videoSize\": [\n" + - " 1920,\n" + - " 1080\n" + - " ]\n" + - " },\n" + - " {\n" + - " \"display\": \"Saal G FullHD Video (Translation)\",\n" + - " \"isTranslated\": true,\n" + - " \"slug\": \"hd-translated\",\n" + - " \"type\": \"video\",\n" + - " \"urls\": {\n" + - " \"hls\": {\n" + - " \"display\": \"HLS\",\n" + - " \"tech\": \"1920x1080, h264+AAC im MPEG-TS-Container via HTTP, 3 MBit/s\",\n" + - " \"url\": \"https://cdn.c3voc.de/hls/s3_translated_hd.m3u8\"\n" + - " },\n" + - " \"webm\": {\n" + - " \"display\": \"WebM\",\n" + - " \"tech\": \"1920x1080, VP8+Vorbis in WebM, 2.8 MBit/s\",\n" + - " \"url\": \"https://cdn.c3voc.de/s3_translated_hd.webm\"\n" + - " }\n" + - " },\n" + - " \"videoSize\": [\n" + - " 1920,\n" + - " 1080\n" + - " ]\n" + - " },\n" + - " {\n" + - " \"display\": \"Saal G SD Video\",\n" + - " \"isTranslated\": false,\n" + - " \"slug\": \"sd-native\",\n" + - " \"type\": \"video\",\n" + - " \"urls\": {\n" + - " \"hls\": {\n" + - " \"display\": \"HLS\",\n" + - " \"tech\": \"1024x576, h264+AAC im MPEG-TS-Container via HTTP, 800 kBit/s\",\n" + - " \"url\": \"https://cdn.c3voc.de/hls/s3_native_sd.m3u8\"\n" + - " },\n" + - " \"webm\": {\n" + - " \"display\": \"WebM\",\n" + - " \"tech\": \"1024x576, VP8+Vorbis in WebM, 800 kBit/s\",\n" + - " \"url\": \"https://cdn.c3voc.de/s3_native_sd.webm\"\n" + - " }\n" + - " },\n" + - " \"videoSize\": [\n" + - " 1024,\n" + - " 576\n" + - " ]\n" + - " },\n" + - " {\n" + - " \"display\": \"Saal G SD Video (Translation)\",\n" + - " \"isTranslated\": true,\n" + - " \"slug\": \"sd-translated\",\n" + - " \"type\": \"video\",\n" + - " \"urls\": {\n" + - " \"hls\": {\n" + - " \"display\": \"HLS\",\n" + - " \"tech\": \"1024x576, h264+AAC im MPEG-TS-Container via HTTP, 800 kBit/s\",\n" + - " \"url\": \"https://cdn.c3voc.de/hls/s3_translated_sd.m3u8\"\n" + - " },\n" + - " \"webm\": {\n" + - " \"display\": \"WebM\",\n" + - " \"tech\": \"1024x576, VP8+Vorbis in WebM, 800 kBit/s\",\n" + - " \"url\": \"https://cdn.c3voc.de/s3_translated_sd.webm\"\n" + - " }\n" + - " },\n" + - " \"videoSize\": [\n" + - " 1024,\n" + - " 576\n" + - " ]\n" + - " },\n" + - " {\n" + - " \"display\": \"Saal G Slides\",\n" + - " \"isTranslated\": false,\n" + - " \"slug\": \"slides-native\",\n" + - " \"type\": \"slides\",\n" + - " \"urls\": {\n" + - " \"hls\": {\n" + - " \"display\": \"HLS\",\n" + - " \"tech\": \"1024x576, h264+AAC im MPEG-TS-Container via HTTP, XXX kBit/s\",\n" + - " \"url\": \"https://cdn.c3voc.de/hls/s3_native_slides.m3u8\"\n" + - " },\n" + - " \"webm\": {\n" + - " \"display\": \"WebM\",\n" + - " \"tech\": \"1024x576, VP8+Vorbis in WebM, XXX kBit/s\",\n" + - " \"url\": \"https://cdn.c3voc.de/s3_native_slides.webm\"\n" + - " }\n" + - " },\n" + - " \"videoSize\": [\n" + - " 1024,\n" + - " 576\n" + - " ]\n" + - " },\n" + - " {\n" + - " \"display\": \"Saal G Slides (Translation)\",\n" + - " \"isTranslated\": true,\n" + - " \"slug\": \"slides-translated\",\n" + - " \"type\": \"slides\",\n" + - " \"urls\": {\n" + - " \"hls\": {\n" + - " \"display\": \"HLS\",\n" + - " \"tech\": \"1024x576, h264+AAC im MPEG-TS-Container via HTTP, XXX kBit/s\",\n" + - " \"url\": \"https://cdn.c3voc.de/hls/s3_translated_slides.m3u8\"\n" + - " },\n" + - " \"webm\": {\n" + - " \"display\": \"WebM\",\n" + - " \"tech\": \"1024x576, VP8+Vorbis in WebM, XXX kBit/s\",\n" + - " \"url\": \"https://cdn.c3voc.de/s3_translated_slides.webm\"\n" + - " }\n" + - " },\n" + - " \"videoSize\": [\n" + - " 1024,\n" + - " 576\n" + - " ]\n" + - " },\n" + - " {\n" + - " \"display\": \"Saal G Audio\",\n" + - " \"isTranslated\": false,\n" + - " \"slug\": \"audio-native\",\n" + - " \"type\": \"audio\",\n" + - " \"urls\": {\n" + - " \"mp3\": {\n" + - " \"display\": \"MP3\",\n" + - " \"tech\": \"MP3-Audio, 96 kBit/s\",\n" + - " \"url\": \"https://cdn.c3voc.de/s3_native.mp3\"\n" + - " },\n" + - " \"opus\": {\n" + - " \"display\": \"Opus\",\n" + - " \"tech\": \"Opus-Audio, 64 kBit/s\",\n" + - " \"url\": \"https://cdn.c3voc.de/s3_native.opus\"\n" + - " }\n" + - " },\n" + - " \"videoSize\": null\n" + - " },\n" + - " {\n" + - " \"display\": \"Saal G Audio (Translation)\",\n" + - " \"isTranslated\": true,\n" + - " \"slug\": \"audio-translated\",\n" + - " \"type\": \"audio\",\n" + - " \"urls\": {\n" + - " \"mp3\": {\n" + - " \"display\": \"MP3\",\n" + - " \"tech\": \"MP3-Audio, 96 kBit/s\",\n" + - " \"url\": \"https://cdn.c3voc.de/s3_translated.mp3\"\n" + - " },\n" + - " \"opus\": {\n" + - " \"display\": \"Opus\",\n" + - " \"tech\": \"Opus-Audio, 64 kBit/s\",\n" + - " \"url\": \"https://cdn.c3voc.de/s3_translated.opus\"\n" + - " }\n" + - " },\n" + - " \"videoSize\": null\n" + - " }\n" + - " ],\n" + - " \"thumb\": \"https://streaming.media.ccc.de/thumbs/s3.png\"\n" + - " },\n" + - " {\n" + - " \"display\": \"Saal 6\",\n" + - " \"link\": \"https://streaming.media.ccc.de/33c3/hall6\",\n" + - " \"schedulename\": \"Saal 6\",\n" + - " \"slug\": \"hall6\",\n" + - " \"streams\": [\n" + - " {\n" + - " \"display\": \"Saal 6 FullHD Video\",\n" + - " \"isTranslated\": false,\n" + - " \"slug\": \"hd-native\",\n" + - " \"type\": \"video\",\n" + - " \"urls\": {\n" + - " \"hls\": {\n" + - " \"display\": \"HLS\",\n" + - " \"tech\": \"1920x1080, h264+AAC im MPEG-TS-Container via HTTP, 3 MBit/s\",\n" + - " \"url\": \"https://cdn.c3voc.de/hls/s4_native_hd.m3u8\"\n" + - " },\n" + - " \"webm\": {\n" + - " \"display\": \"WebM\",\n" + - " \"tech\": \"1920x1080, VP8+Vorbis in WebM, 2.8 MBit/s\",\n" + - " \"url\": \"https://cdn.c3voc.de/s4_native_hd.webm\"\n" + - " }\n" + - " },\n" + - " \"videoSize\": [\n" + - " 1920,\n" + - " 1080\n" + - " ]\n" + - " },\n" + - " {\n" + - " \"display\": \"Saal 6 FullHD Video (Translation)\",\n" + - " \"isTranslated\": true,\n" + - " \"slug\": \"hd-translated\",\n" + - " \"type\": \"video\",\n" + - " \"urls\": {\n" + - " \"hls\": {\n" + - " \"display\": \"HLS\",\n" + - " \"tech\": \"1920x1080, h264+AAC im MPEG-TS-Container via HTTP, 3 MBit/s\",\n" + - " \"url\": \"https://cdn.c3voc.de/hls/s4_translated_hd.m3u8\"\n" + - " },\n" + - " \"webm\": {\n" + - " \"display\": \"WebM\",\n" + - " \"tech\": \"1920x1080, VP8+Vorbis in WebM, 2.8 MBit/s\",\n" + - " \"url\": \"https://cdn.c3voc.de/s4_translated_hd.webm\"\n" + - " }\n" + - " },\n" + - " \"videoSize\": [\n" + - " 1920,\n" + - " 1080\n" + - " ]\n" + - " },\n" + - " {\n" + - " \"display\": \"Saal 6 SD Video\",\n" + - " \"isTranslated\": false,\n" + - " \"slug\": \"sd-native\",\n" + - " \"type\": \"video\",\n" + - " \"urls\": {\n" + - " \"hls\": {\n" + - " \"display\": \"HLS\",\n" + - " \"tech\": \"1024x576, h264+AAC im MPEG-TS-Container via HTTP, 800 kBit/s\",\n" + - " \"url\": \"https://cdn.c3voc.de/hls/s4_native_sd.m3u8\"\n" + - " },\n" + - " \"webm\": {\n" + - " \"display\": \"WebM\",\n" + - " \"tech\": \"1024x576, VP8+Vorbis in WebM, 800 kBit/s\",\n" + - " \"url\": \"https://cdn.c3voc.de/s4_native_sd.webm\"\n" + - " }\n" + - " },\n" + - " \"videoSize\": [\n" + - " 1024,\n" + - " 576\n" + - " ]\n" + - " },\n" + - " {\n" + - " \"display\": \"Saal 6 SD Video (Translation)\",\n" + - " \"isTranslated\": true,\n" + - " \"slug\": \"sd-translated\",\n" + - " \"type\": \"video\",\n" + - " \"urls\": {\n" + - " \"hls\": {\n" + - " \"display\": \"HLS\",\n" + - " \"tech\": \"1024x576, h264+AAC im MPEG-TS-Container via HTTP, 800 kBit/s\",\n" + - " \"url\": \"https://cdn.c3voc.de/hls/s4_translated_sd.m3u8\"\n" + - " },\n" + - " \"webm\": {\n" + - " \"display\": \"WebM\",\n" + - " \"tech\": \"1024x576, VP8+Vorbis in WebM, 800 kBit/s\",\n" + - " \"url\": \"https://cdn.c3voc.de/s4_translated_sd.webm\"\n" + - " }\n" + - " },\n" + - " \"videoSize\": [\n" + - " 1024,\n" + - " 576\n" + - " ]\n" + - " },\n" + - " {\n" + - " \"display\": \"Saal 6 Slides\",\n" + - " \"isTranslated\": false,\n" + - " \"slug\": \"slides-native\",\n" + - " \"type\": \"slides\",\n" + - " \"urls\": {\n" + - " \"hls\": {\n" + - " \"display\": \"HLS\",\n" + - " \"tech\": \"1024x576, h264+AAC im MPEG-TS-Container via HTTP, XXX kBit/s\",\n" + - " \"url\": \"https://cdn.c3voc.de/hls/s4_native_slides.m3u8\"\n" + - " },\n" + - " \"webm\": {\n" + - " \"display\": \"WebM\",\n" + - " \"tech\": \"1024x576, VP8+Vorbis in WebM, XXX kBit/s\",\n" + - " \"url\": \"https://cdn.c3voc.de/s4_native_slides.webm\"\n" + - " }\n" + - " },\n" + - " \"videoSize\": [\n" + - " 1024,\n" + - " 576\n" + - " ]\n" + - " },\n" + - " {\n" + - " \"display\": \"Saal 6 Slides (Translation)\",\n" + - " \"isTranslated\": true,\n" + - " \"slug\": \"slides-translated\",\n" + - " \"type\": \"slides\",\n" + - " \"urls\": {\n" + - " \"hls\": {\n" + - " \"display\": \"HLS\",\n" + - " \"tech\": \"1024x576, h264+AAC im MPEG-TS-Container via HTTP, XXX kBit/s\",\n" + - " \"url\": \"https://cdn.c3voc.de/hls/s4_translated_slides.m3u8\"\n" + - " },\n" + - " \"webm\": {\n" + - " \"display\": \"WebM\",\n" + - " \"tech\": \"1024x576, VP8+Vorbis in WebM, XXX kBit/s\",\n" + - " \"url\": \"https://cdn.c3voc.de/s4_translated_slides.webm\"\n" + - " }\n" + - " },\n" + - " \"videoSize\": [\n" + - " 1024,\n" + - " 576\n" + - " ]\n" + - " },\n" + - " {\n" + - " \"display\": \"Saal 6 Audio\",\n" + - " \"isTranslated\": false,\n" + - " \"slug\": \"audio-native\",\n" + - " \"type\": \"audio\",\n" + - " \"urls\": {\n" + - " \"mp3\": {\n" + - " \"display\": \"MP3\",\n" + - " \"tech\": \"MP3-Audio, 96 kBit/s\",\n" + - " \"url\": \"https://cdn.c3voc.de/s4_native.mp3\"\n" + - " },\n" + - " \"opus\": {\n" + - " \"display\": \"Opus\",\n" + - " \"tech\": \"Opus-Audio, 64 kBit/s\",\n" + - " \"url\": \"https://cdn.c3voc.de/s4_native.opus\"\n" + - " }\n" + - " },\n" + - " \"videoSize\": null\n" + - " },\n" + - " {\n" + - " \"display\": \"Saal 6 Audio (Translation)\",\n" + - " \"isTranslated\": true,\n" + - " \"slug\": \"audio-translated\",\n" + - " \"type\": \"audio\",\n" + - " \"urls\": {\n" + - " \"mp3\": {\n" + - " \"display\": \"MP3\",\n" + - " \"tech\": \"MP3-Audio, 96 kBit/s\",\n" + - " \"url\": \"https://cdn.c3voc.de/s4_translated.mp3\"\n" + - " },\n" + - " \"opus\": {\n" + - " \"display\": \"Opus\",\n" + - " \"tech\": \"Opus-Audio, 64 kBit/s\",\n" + - " \"url\": \"https://cdn.c3voc.de/s4_translated.opus\"\n" + - " }\n" + - " },\n" + - " \"videoSize\": null\n" + - " }\n" + - " ],\n" + - " \"thumb\": \"https://streaming.media.ccc.de/thumbs/s4.png\"\n" + - " },\n" + - " {\n" + - " \"display\": \"Sendezentrum\",\n" + - " \"link\": \"https://streaming.media.ccc.de/33c3/sendezentrum\",\n" + - " \"schedulename\": \"Sendezentrum\",\n" + - " \"slug\": \"sendezentrum\",\n" + - " \"streams\": [\n" + - " {\n" + - " \"display\": \"Sendezentrum FullHD Video\",\n" + - " \"isTranslated\": false,\n" + - " \"slug\": \"hd-native\",\n" + - " \"type\": \"video\",\n" + - " \"urls\": {\n" + - " \"hls\": {\n" + - " \"display\": \"HLS\",\n" + - " \"tech\": \"1920x1080, h264+AAC im MPEG-TS-Container via HTTP, 3 MBit/s\",\n" + - " \"url\": \"https://cdn.c3voc.de/hls/s5_native_hd.m3u8\"\n" + - " },\n" + - " \"webm\": {\n" + - " \"display\": \"WebM\",\n" + - " \"tech\": \"1920x1080, VP8+Vorbis in WebM, 2.8 MBit/s\",\n" + - " \"url\": \"https://cdn.c3voc.de/s5_native_hd.webm\"\n" + - " }\n" + - " },\n" + - " \"videoSize\": [\n" + - " 1920,\n" + - " 1080\n" + - " ]\n" + - " },\n" + - " {\n" + - " \"display\": \"Sendezentrum SD Video\",\n" + - " \"isTranslated\": false,\n" + - " \"slug\": \"sd-native\",\n" + - " \"type\": \"video\",\n" + - " \"urls\": {\n" + - " \"hls\": {\n" + - " \"display\": \"HLS\",\n" + - " \"tech\": \"1024x576, h264+AAC im MPEG-TS-Container via HTTP, 800 kBit/s\",\n" + - " \"url\": \"https://cdn.c3voc.de/hls/s5_native_sd.m3u8\"\n" + - " },\n" + - " \"webm\": {\n" + - " \"display\": \"WebM\",\n" + - " \"tech\": \"1024x576, VP8+Vorbis in WebM, 800 kBit/s\",\n" + - " \"url\": \"https://cdn.c3voc.de/s5_native_sd.webm\"\n" + - " }\n" + - " },\n" + - " \"videoSize\": [\n" + - " 1024,\n" + - " 576\n" + - " ]\n" + - " },\n" + - " {\n" + - " \"display\": \"Sendezentrum Audio\",\n" + - " \"isTranslated\": false,\n" + - " \"slug\": \"audio-native\",\n" + - " \"type\": \"audio\",\n" + - " \"urls\": {\n" + - " \"mp3\": {\n" + - " \"display\": \"MP3\",\n" + - " \"tech\": \"MP3-Audio, 96 kBit/s\",\n" + - " \"url\": \"https://cdn.c3voc.de/s5_native.mp3\"\n" + - " },\n" + - " \"opus\": {\n" + - " \"display\": \"Opus\",\n" + - " \"tech\": \"Opus-Audio, 64 kBit/s\",\n" + - " \"url\": \"https://cdn.c3voc.de/s5_native.opus\"\n" + - " }\n" + - " },\n" + - " \"videoSize\": null\n" + - " }\n" + - " ],\n" + - " \"thumb\": \"https://streaming.media.ccc.de/thumbs/s5.png\"\n" + - " }\n" + - " ]\n" + - " },\n" + - " {\n" + - " \"group\": \"Live Music\",\n" + - " \"rooms\": [\n" + - " {\n" + - " \"display\": \"Section 9\",\n" + - " \"link\": \"https://streaming.media.ccc.de/33c3/section-9\",\n" + - " \"schedulename\": \"Section 9\",\n" + - " \"slug\": \"section-9\",\n" + - " \"streams\": [\n" + - " {\n" + - " \"display\": \"Section 9 Radio\",\n" + - " \"isTranslated\": false,\n" + - " \"slug\": \"music-native\",\n" + - " \"type\": \"music\",\n" + - " \"urls\": {\n" + - " \"mp3\": {\n" + - " \"display\": \"MP3\",\n" + - " \"tech\": \"MP3-Audio, 192 kBit/s\",\n" + - " \"url\": \"https://cdn.c3voc.de/ambient.mp3\"\n" + - " },\n" + - " \"opus\": {\n" + - " \"display\": \"Opus\",\n" + - " \"tech\": \"Opus-Audio, 96 kBit/s\",\n" + - " \"url\": \"https://cdn.c3voc.de/ambient.opus\"\n" + - " }\n" + - " },\n" + - " \"videoSize\": null\n" + - " }\n" + - " ],\n" + - " \"thumb\": \"https://streaming.media.ccc.de/thumbs/ambient.png\"\n" + - " },\n" + - " {\n" + - " \"display\": \"DisKo\",\n" + - " \"link\": \"https://streaming.media.ccc.de/33c3/disko\",\n" + - " \"schedulename\": \"DisKo\",\n" + - " \"slug\": \"disko\",\n" + - " \"streams\": [\n" + - " {\n" + - " \"display\": \"DisKo Radio\",\n" + - " \"isTranslated\": false,\n" + - " \"slug\": \"music-native\",\n" + - " \"type\": \"music\",\n" + - " \"urls\": {\n" + - " \"mp3\": {\n" + - " \"display\": \"MP3\",\n" + - " \"tech\": \"MP3-Audio, 192 kBit/s\",\n" + - " \"url\": \"https://cdn.c3voc.de/lounge.mp3\"\n" + - " },\n" + - " \"opus\": {\n" + - " \"display\": \"Opus\",\n" + - " \"tech\": \"Opus-Audio, 96 kBit/s\",\n" + - " \"url\": \"https://cdn.c3voc.de/lounge.opus\"\n" + - " }\n" + - " },\n" + - " \"videoSize\": null\n" + - " }\n" + - " ],\n" + - " \"thumb\": \"https://streaming.media.ccc.de/thumbs/lounge.png\"\n" + - " },\n" + - " {\n" + - " \"display\": \"Schneekugel\",\n" + - " \"link\": \"https://streaming.media.ccc.de/33c3/schneekugel\",\n" + - " \"schedulename\": \"Schneekugel\",\n" + - " \"slug\": \"schneekugel\",\n" + - " \"streams\": [\n" + - " {\n" + - " \"display\": \"Schneekugel Radio\",\n" + - " \"isTranslated\": false,\n" + - " \"slug\": \"music-native\",\n" + - " \"type\": \"music\",\n" + - " \"urls\": {\n" + - " \"mp3\": {\n" + - " \"display\": \"MP3\",\n" + - " \"tech\": \"MP3-Audio, 192 kBit/s\",\n" + - " \"url\": \"https://cdn.c3voc.de/dome.mp3\"\n" + - " },\n" + - " \"opus\": {\n" + - " \"display\": \"Opus\",\n" + - " \"tech\": \"Opus-Audio, 96 kBit/s\",\n" + - " \"url\": \"https://cdn.c3voc.de/dome.opus\"\n" + - " }\n" + - " },\n" + - " \"videoSize\": null\n" + - " }\n" + - " ],\n" + - " \"thumb\": \"https://streaming.media.ccc.de/thumbs/dome.png\"\n" + - " },\n" + - " {\n" + - " \"display\": \"Chaoswest\",\n" + - " \"link\": \"https://streaming.media.ccc.de/33c3/chaoswest\",\n" + - " \"schedulename\": \"Chaoswest\",\n" + - " \"slug\": \"chaoswest\",\n" + - " \"streams\": [\n" + - " {\n" + - " \"display\": \"Chaoswest Radio\",\n" + - " \"isTranslated\": false,\n" + - " \"slug\": \"music-native\",\n" + - " \"type\": \"music\",\n" + - " \"urls\": {\n" + - " \"mp3\": {\n" + - " \"display\": \"MP3\",\n" + - " \"tech\": \"MP3-Audio, 192 kBit/s\",\n" + - " \"url\": \"https://cdn.c3voc.de/chaoswest_lounge.mp3\"\n" + - " },\n" + - " \"opus\": {\n" + - " \"display\": \"Opus\",\n" + - " \"tech\": \"Opus-Audio, 96 kBit/s\",\n" + - " \"url\": \"https://cdn.c3voc.de/chaoswest_lounge.opus\"\n" + - " }\n" + - " },\n" + - " \"videoSize\": null\n" + - " }\n" + - " ],\n" + - " \"thumb\": \"https://streaming.media.ccc.de/thumbs/chaoswest_lounge.png\"\n" + - " }\n" + - " ]\n" + - " }\n" + - " ],\n" + - " \"keywords\": \"33C3, Hacking, Chaos Computer Club, Video, Music, Podcast, Media, Streaming, Hacker, Hamburg, Works, For, Me, Chaos, Everywhere\",\n" + - " \"slug\": \"33c3\",\n" + - " \"startsAt\": \"2016-12-27T05:00:00+0000\"\n" + - " }\n" + - "]\n";*/ - try { - doc = JsonParser.array().from(site); - } catch (JsonParserException jpe) { - throw new ExtractionException("Could not parse json.", jpe); - } + doc = MediaCCCParsingHelper.getLiveStreams(downloader, getExtractorLocalization()); } @Nonnull @@ -1955,7 +41,7 @@ public class MediaCCCLiveStreamKiosk extends KioskExtractor { final JsonArray rooms = groups.getObject(g).getArray("rooms"); for (int r = 0; r < rooms.size(); r++) { final JsonObject room = rooms.getObject(r); - collector.commit(new MediaCCCLiveStreamExtractor(conference, group, room)); + collector.commit(new MediaCCCLiveStreamKioskExtractor(conference, group, room)); } } diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/extractors/MediaCCCLiveStreamKioskExtractor.java b/extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/extractors/MediaCCCLiveStreamKioskExtractor.java new file mode 100644 index 000000000..941528708 --- /dev/null +++ b/extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/extractors/MediaCCCLiveStreamKioskExtractor.java @@ -0,0 +1,87 @@ +package org.schabi.newpipe.extractor.services.media_ccc.extractors; + +import com.grack.nanojson.JsonObject; +import org.schabi.newpipe.extractor.exceptions.ParsingException; +import org.schabi.newpipe.extractor.localization.DateWrapper; +import org.schabi.newpipe.extractor.stream.StreamInfoItemExtractor; +import org.schabi.newpipe.extractor.stream.StreamType; + +import javax.annotation.Nullable; + +public class MediaCCCLiveStreamKioskExtractor implements StreamInfoItemExtractor { + + private final JsonObject conferenceInfo; + private final String group; + private final JsonObject roomInfo; + + public MediaCCCLiveStreamKioskExtractor(final JsonObject conferenceInfo, final String group, + final JsonObject roomInfo) { + this.conferenceInfo = conferenceInfo; + this.group = group; + this.roomInfo = roomInfo; + } + + @Override + public String getName() throws ParsingException { + return roomInfo.getString("schedulename"); + } + + @Override + public String getUrl() throws ParsingException { + return roomInfo.getString("link"); + } + + @Override + public String getThumbnailUrl() throws ParsingException { + return roomInfo.getString("thumb"); + } + + @Override + public StreamType getStreamType() throws ParsingException { + boolean isVideo = false; + for (Object stream : roomInfo.getArray("streams")) { + if ("video".equals(((JsonObject) stream).getString("type"))) { + isVideo = true; + break; + } + } + return isVideo ? StreamType.LIVE_STREAM : StreamType.AUDIO_LIVE_STREAM; + } + + @Override + public boolean isAd() throws ParsingException { + return false; + } + + @Override + public long getDuration() throws ParsingException { + return 0; + } + + @Override + public long getViewCount() throws ParsingException { + return -1; + } + + @Override + public String getUploaderName() throws ParsingException { + return conferenceInfo.getString("conference"); + } + + @Override + public String getUploaderUrl() throws ParsingException { + return "https://media.ccc.de/c/" + conferenceInfo.getString("slug"); + } + + @Nullable + @Override + public String getTextualUploadDate() throws ParsingException { + return null; + } + + @Nullable + @Override + public DateWrapper getUploadDate() throws ParsingException { + return null; + } +} diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/extractors/MediaCCCParsingHelper.java b/extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/extractors/MediaCCCParsingHelper.java index b6879fcdf..421482434 100644 --- a/extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/extractors/MediaCCCParsingHelper.java +++ b/extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/extractors/MediaCCCParsingHelper.java @@ -1,11 +1,23 @@ package org.schabi.newpipe.extractor.services.media_ccc.extractors; +import com.grack.nanojson.JsonArray; +import com.grack.nanojson.JsonObject; +import com.grack.nanojson.JsonParser; +import com.grack.nanojson.JsonParserException; +import org.schabi.newpipe.extractor.downloader.Downloader; +import org.schabi.newpipe.extractor.exceptions.ExtractionException; import org.schabi.newpipe.extractor.exceptions.ParsingException; +import org.schabi.newpipe.extractor.exceptions.ReCaptchaException; +import org.schabi.newpipe.extractor.localization.Localization; +import java.io.IOException; import java.time.OffsetDateTime; import java.time.format.DateTimeParseException; +import java.util.regex.Pattern; public final class MediaCCCParsingHelper { + private static JsonArray liveStreams = null; + private MediaCCCParsingHelper() { } public static OffsetDateTime parseDateFrom(final String textualUploadDate) throws ParsingException { @@ -15,4 +27,24 @@ public final class MediaCCCParsingHelper { throw new ParsingException("Could not parse date: \"" + textualUploadDate + "\"", e); } } + + public static boolean isLiveStreamId(final String url) { + final String pattern = "\\w+/\\w+"; + return Pattern.matches(pattern, url); // {conference_slug}/{room_slug} + } + + public static JsonArray getLiveStreams(final Downloader downloader, final Localization localization) throws ExtractionException { + if (liveStreams == null) { + try { + final String site = downloader.get("https://streaming.media.ccc.de/streams/v2.json", + localization).responseBody(); + liveStreams = JsonParser.array().from(site); + } catch (IOException | ReCaptchaException e) { + throw new ExtractionException("Could not get live stream JSON.", e); + } catch (JsonParserException e) { + throw new ExtractionException("Could not parse JSON.", e); + } + } + return liveStreams; + } } diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/linkHandler/MediaCCCLiveStreamListLinkHandlerFactory.java b/extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/linkHandler/MediaCCCLiveListLinkHandlerFactory.java similarity index 76% rename from extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/linkHandler/MediaCCCLiveStreamListLinkHandlerFactory.java rename to extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/linkHandler/MediaCCCLiveListLinkHandlerFactory.java index 3dc5c46ea..84009c2bf 100644 --- a/extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/linkHandler/MediaCCCLiveStreamListLinkHandlerFactory.java +++ b/extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/linkHandler/MediaCCCLiveListLinkHandlerFactory.java @@ -6,8 +6,8 @@ import org.schabi.newpipe.extractor.linkhandler.ListLinkHandlerFactory; import java.util.List; import java.util.regex.Pattern; -public class MediaCCCLiveStreamListLinkHandlerFactory extends ListLinkHandlerFactory { - private static final String streamPattern = "^(https?://)?streaming.media.ccc.de$"; +public class MediaCCCLiveListLinkHandlerFactory extends ListLinkHandlerFactory { + private static final String streamPattern = "^(?:https?://)?media\\.ccc\\.de/live$"; @Override public String getId(String url) throws ParsingException { @@ -22,6 +22,6 @@ public class MediaCCCLiveStreamListLinkHandlerFactory extends ListLinkHandlerFac @Override public String getUrl(String id, List contentFilter, String sortFilter) throws ParsingException { // FIXME: wrong URL; should be https://streaming.media.ccc.de/{conference_slug}/{room_slug} - return "https://streaming.media.ccc.de/" + id; + return "https://media.ccc.de/live"; } } diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/linkHandler/MediaCCCLiveStreamLinkHandlerFactory.java b/extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/linkHandler/MediaCCCLiveStreamLinkHandlerFactory.java new file mode 100644 index 000000000..9c88e026f --- /dev/null +++ b/extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/linkHandler/MediaCCCLiveStreamLinkHandlerFactory.java @@ -0,0 +1,30 @@ +package org.schabi.newpipe.extractor.services.media_ccc.linkHandler; + +import org.schabi.newpipe.extractor.exceptions.ParsingException; +import org.schabi.newpipe.extractor.linkhandler.LinkHandlerFactory; +import org.schabi.newpipe.extractor.utils.Parser; + +public class MediaCCCLiveStreamLinkHandlerFactory extends LinkHandlerFactory { + public static final String VIDEO_API_ENDPOINT = "https://api.media.ccc.de/public/events/"; + private static final String VIDEO_PATH = "https://streaming.media.ccc.de/v/"; + private static final String ID_PATTERN = "(?:(?:(?:api\\.)?media\\.ccc\\.de/public/events/)|(?:media\\.ccc\\.de/v/))([^/?&#]*)"; + + @Override + public String getId(final String url) throws ParsingException { + return Parser.matchGroup1(ID_PATTERN, url); + } + + @Override + public String getUrl(final String id) throws ParsingException { + return VIDEO_PATH + id; + } + + @Override + public boolean onAcceptUrl(final String url) { + try { + return getId(url) != null; + } catch (ParsingException e) { + return false; + } + } +} diff --git a/extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/linkHandler/MediaCCCStreamLinkHandlerFactory.java b/extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/linkHandler/MediaCCCStreamLinkHandlerFactory.java index fa9ac4829..cb2f72fc9 100644 --- a/extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/linkHandler/MediaCCCStreamLinkHandlerFactory.java +++ b/extractor/src/main/java/org/schabi/newpipe/extractor/services/media_ccc/linkHandler/MediaCCCStreamLinkHandlerFactory.java @@ -2,20 +2,36 @@ package org.schabi.newpipe.extractor.services.media_ccc.linkHandler; import org.schabi.newpipe.extractor.exceptions.ParsingException; import org.schabi.newpipe.extractor.linkhandler.LinkHandlerFactory; +import org.schabi.newpipe.extractor.services.media_ccc.extractors.MediaCCCParsingHelper; import org.schabi.newpipe.extractor.utils.Parser; public class MediaCCCStreamLinkHandlerFactory extends LinkHandlerFactory { public static final String VIDEO_API_ENDPOINT = "https://api.media.ccc.de/public/events/"; private static final String VIDEO_PATH = "https://media.ccc.de/v/"; - private static final String ID_PATTERN = "(?:(?:(?:api\\.)?media\\.ccc\\.de/public/events/)|(?:media\\.ccc\\.de/v/))([^/?&#]*)"; + private static final String RECORDING_ID_PATTERN = "(?:(?:(?:api\\.)?media\\.ccc\\.de/public/events/)|(?:media\\.ccc\\.de/v/))([^/?&#]*)"; + private static final String LIVE_STREAM_API_ENDPOINT = "https://streaming.media.ccc.de/streams/v2.json"; + private static final String LIVE_STREAM_PATH = "https://streaming.media.ccc.de/"; + private static final String LIVE_STREAM_ID_PATTERN = "streaming\\.media\\.ccc\\.de\\/(\\w+\\/\\w+)"; @Override public String getId(final String url) throws ParsingException { - return Parser.matchGroup1(ID_PATTERN, url); + String streamId = null; + try { + streamId = Parser.matchGroup1(LIVE_STREAM_ID_PATTERN, url); + } catch (Parser.RegexException ignored) { + + } + if (streamId == null) { + return Parser.matchGroup1(RECORDING_ID_PATTERN, url); + } + return streamId; } @Override public String getUrl(final String id) throws ParsingException { + if (MediaCCCParsingHelper.isLiveStreamId(id)) { + return LIVE_STREAM_PATH + id; + } return VIDEO_PATH + id; }