UNPKG

matrix-react-sdk

Version:
200 lines (192 loc) 33.4 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports.PlaybackQueue = void 0; var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty")); var _matrix = require("matrix-js-sdk/src/matrix"); var _logger = require("matrix-js-sdk/src/logger"); var _Playback = require("./Playback"); var _AsyncStore = require("../stores/AsyncStore"); var _MatrixClientPeg = require("../MatrixClientPeg"); var _arrays = require("../utils/arrays"); var _PlaybackManager = require("./PlaybackManager"); var _EventUtils = require("../utils/EventUtils"); var _SDKContext = require("../contexts/SDKContext"); var _PlaybackQueue; /* Copyright 2024 New Vector Ltd. Copyright 2021, 2022 The Matrix.org Foundation C.I.C. SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only Please see LICENSE files in the repository root for full details. */ /** * Audio playback queue management for a given room. This keeps track of where the user * was at for each playback, what order the playbacks were played in, and triggers subsequent * playbacks. * * Currently this is only intended to be used by voice messages. * * The primary mechanics are: * * Persisted clock state for each playback instance (tied to Event ID). * * Limited memory of playback order (see code; not persisted). * * Autoplay of next eligible playback instance. */ class PlaybackQueue { // event IDs constructor(room) { // keyed by room ID (0, _defineProperty2.default)(this, "playbacks", new Map()); // keyed by event ID (0, _defineProperty2.default)(this, "clockStates", new Map()); // keyed by event ID (0, _defineProperty2.default)(this, "playbackIdOrder", []); // event IDs, last == current (0, _defineProperty2.default)(this, "currentPlaybackId", null); // event ID, broken out from above for ease of use (0, _defineProperty2.default)(this, "recentFullPlays", new Set()); this.room = room; this.loadClocks(); _SDKContext.SdkContextClass.instance.roomViewStore.addRoomListener(this.room.roomId, isActive => { if (!isActive) return; // Reset the state of the playbacks before they start mounting and enqueuing updates. // We reset the entirety of the queue, including order, to ensure the user isn't left // confused with what order the messages are playing in. this.currentPlaybackId = null; // this in particular stops autoplay when the room is switched to this.recentFullPlays = new Set(); this.playbackIdOrder = []; }); } static forRoom(roomId) { const cli = _MatrixClientPeg.MatrixClientPeg.safeGet(); const room = cli.getRoom(roomId); if (!room) throw new Error("Unknown room"); if (PlaybackQueue.queues.has(room.roomId)) { return PlaybackQueue.queues.get(room.roomId); } const queue = new PlaybackQueue(room); PlaybackQueue.queues.set(room.roomId, queue); return queue; } persistClocks() { localStorage.setItem(`mx_voice_message_clocks_${this.room.roomId}`, JSON.stringify(Array.from(this.clockStates.entries()))); } loadClocks() { const val = localStorage.getItem(`mx_voice_message_clocks_${this.room.roomId}`); if (!!val) { this.clockStates = new Map(JSON.parse(val)); } } unsortedEnqueue(mxEvent, playback) { // We don't ever detach our listeners: we expect the Playback to clean up for us this.playbacks.set(mxEvent.getId(), playback); playback.on(_AsyncStore.UPDATE_EVENT, state => this.onPlaybackStateChange(playback, mxEvent, state)); playback.clockInfo.liveData.onUpdate(clock => this.onPlaybackClock(playback, mxEvent, clock)); } onPlaybackStateChange(playback, mxEvent, newState) { // Remember where the user got to in playback const wasLastPlaying = this.currentPlaybackId === mxEvent.getId(); if (newState === _Playback.PlaybackState.Stopped && this.clockStates.has(mxEvent.getId()) && !wasLastPlaying) { // noinspection JSIgnoredPromiseFromCall playback.skipTo(this.clockStates.get(mxEvent.getId())); } else if (newState === _Playback.PlaybackState.Stopped) { // Remove the now-useless clock for some space savings this.clockStates.delete(mxEvent.getId()); if (wasLastPlaying && this.currentPlaybackId) { this.recentFullPlays.add(this.currentPlaybackId); const orderClone = (0, _arrays.arrayFastClone)(this.playbackIdOrder); const last = orderClone.pop(); if (last === this.currentPlaybackId) { const next = orderClone.pop(); if (next) { const instance = this.playbacks.get(next); if (!instance) { _logger.logger.warn("Voice message queue desync: Missing playback for next message: " + `Current=${this.currentPlaybackId} Last=${last} Next=${next}`); } else { this.playbackIdOrder = orderClone; _PlaybackManager.PlaybackManager.instance.pauseAllExcept(instance); // This should cause a Play event, which will re-populate our playback order // and update our current playback ID. // noinspection JSIgnoredPromiseFromCall instance.play(); } } else { // else no explicit next event, so find an event we haven't played that comes next. The live // timeline is already most recent last, so we can iterate down that. const timeline = (0, _arrays.arrayFastClone)(this.room.getLiveTimeline().getEvents()); let scanForVoiceMessage = false; let nextEv; for (const event of timeline) { if (event.getId() === mxEvent.getId()) { scanForVoiceMessage = true; continue; } if (!scanForVoiceMessage) continue; if (!(0, _EventUtils.isVoiceMessage)(event)) { const evType = event.getType(); if (evType !== _matrix.EventType.RoomMessage && evType !== _matrix.EventType.Sticker) { continue; // Event can be skipped for automatic playback consideration } break; // Stop automatic playback: next useful event is not a voice message } const havePlayback = this.playbacks.has(event.getId()); const isRecentlyCompleted = this.recentFullPlays.has(event.getId()); if (havePlayback && !isRecentlyCompleted) { nextEv = event; break; } } if (!nextEv) { // if we don't have anywhere to go, reset the recent playback queue so the user // can start a new chain of playbacks. this.recentFullPlays = new Set(); this.playbackIdOrder = []; } else { this.playbackIdOrder = orderClone; const instance = this.playbacks.get(nextEv.getId()); _PlaybackManager.PlaybackManager.instance.pauseAllExcept(instance); // This should cause a Play event, which will re-populate our playback order // and update our current playback ID. // noinspection JSIgnoredPromiseFromCall instance?.play(); } } } else { _logger.logger.warn("Voice message queue desync: Expected playback stop to be last in order. " + `Current=${this.currentPlaybackId} Last=${last} EventID=${mxEvent.getId()}`); } } } if (newState === _Playback.PlaybackState.Playing) { const order = this.playbackIdOrder; if (this.currentPlaybackId !== mxEvent.getId() && !!this.currentPlaybackId) { if (order.length === 0 || order[order.length - 1] !== this.currentPlaybackId) { const lastInstance = this.playbacks.get(this.currentPlaybackId); if (lastInstance && [_Playback.PlaybackState.Playing, _Playback.PlaybackState.Paused].includes(lastInstance.currentState)) { order.push(this.currentPlaybackId); } } } this.currentPlaybackId = mxEvent.getId(); if (order.length === 0 || order[order.length - 1] !== this.currentPlaybackId) { order.push(this.currentPlaybackId); } } // Only persist clock information on pause/stop (end) to avoid overwhelming the storage. // This should get triggered from normal voice message component unmount due to the playback // stopping itself for cleanup. if (newState === _Playback.PlaybackState.Paused || newState === _Playback.PlaybackState.Stopped) { this.persistClocks(); } } onPlaybackClock(playback, mxEvent, clocks) { if (playback.currentState === _Playback.PlaybackState.Decoding) return; // ignore pre-ready values if (playback.currentState !== _Playback.PlaybackState.Stopped) { this.clockStates.set(mxEvent.getId(), clocks[0]); // [0] is the current seek position } } } exports.PlaybackQueue = PlaybackQueue; _PlaybackQueue = PlaybackQueue; (0, _defineProperty2.default)(PlaybackQueue, "queues", new Map()); //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJfbWF0cml4IiwicmVxdWlyZSIsIl9sb2dnZXIiLCJfUGxheWJhY2siLCJfQXN5bmNTdG9yZSIsIl9NYXRyaXhDbGllbnRQZWciLCJfYXJyYXlzIiwiX1BsYXliYWNrTWFuYWdlciIsIl9FdmVudFV0aWxzIiwiX1NES0NvbnRleHQiLCJfUGxheWJhY2tRdWV1ZSIsIlBsYXliYWNrUXVldWUiLCJjb25zdHJ1Y3RvciIsInJvb20iLCJfZGVmaW5lUHJvcGVydHkyIiwiZGVmYXVsdCIsIk1hcCIsIlNldCIsImxvYWRDbG9ja3MiLCJTZGtDb250ZXh0Q2xhc3MiLCJpbnN0YW5jZSIsInJvb21WaWV3U3RvcmUiLCJhZGRSb29tTGlzdGVuZXIiLCJyb29tSWQiLCJpc0FjdGl2ZSIsImN1cnJlbnRQbGF5YmFja0lkIiwicmVjZW50RnVsbFBsYXlzIiwicGxheWJhY2tJZE9yZGVyIiwiZm9yUm9vbSIsImNsaSIsIk1hdHJpeENsaWVudFBlZyIsInNhZmVHZXQiLCJnZXRSb29tIiwiRXJyb3IiLCJxdWV1ZXMiLCJoYXMiLCJnZXQiLCJxdWV1ZSIsInNldCIsInBlcnNpc3RDbG9ja3MiLCJsb2NhbFN0b3JhZ2UiLCJzZXRJdGVtIiwiSlNPTiIsInN0cmluZ2lmeSIsIkFycmF5IiwiZnJvbSIsImNsb2NrU3RhdGVzIiwiZW50cmllcyIsInZhbCIsImdldEl0ZW0iLCJwYXJzZSIsInVuc29ydGVkRW5xdWV1ZSIsIm14RXZlbnQiLCJwbGF5YmFjayIsInBsYXliYWNrcyIsImdldElkIiwib24iLCJVUERBVEVfRVZFTlQiLCJzdGF0ZSIsIm9uUGxheWJhY2tTdGF0ZUNoYW5nZSIsImNsb2NrSW5mbyIsImxpdmVEYXRhIiwib25VcGRhdGUiLCJjbG9jayIsIm9uUGxheWJhY2tDbG9jayIsIm5ld1N0YXRlIiwid2FzTGFzdFBsYXlpbmciLCJQbGF5YmFja1N0YXRlIiwiU3RvcHBlZCIsInNraXBUbyIsImRlbGV0ZSIsImFkZCIsIm9yZGVyQ2xvbmUiLCJhcnJheUZhc3RDbG9uZSIsImxhc3QiLCJwb3AiLCJuZXh0IiwibG9nZ2VyIiwid2FybiIsIlBsYXliYWNrTWFuYWdlciIsInBhdXNlQWxsRXhjZXB0IiwicGxheSIsInRpbWVsaW5lIiwiZ2V0TGl2ZVRpbWVsaW5lIiwiZ2V0RXZlbnRzIiwic2NhbkZvclZvaWNlTWVzc2FnZSIsIm5leHRFdiIsImV2ZW50IiwiaXNWb2ljZU1lc3NhZ2UiLCJldlR5cGUiLCJnZXRUeXBlIiwiRXZlbnRUeXBlIiwiUm9vbU1lc3NhZ2UiLCJTdGlja2VyIiwiaGF2ZVBsYXliYWNrIiwiaXNSZWNlbnRseUNvbXBsZXRlZCIsIlBsYXlpbmciLCJvcmRlciIsImxlbmd0aCIsImxhc3RJbnN0YW5jZSIsIlBhdXNlZCIsImluY2x1ZGVzIiwiY3VycmVudFN0YXRlIiwicHVzaCIsImNsb2NrcyIsIkRlY29kaW5nIiwiZXhwb3J0cyJdLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9hdWRpby9QbGF5YmFja1F1ZXVlLnRzIl0sInNvdXJjZXNDb250ZW50IjpbIi8qXG5Db3B5cmlnaHQgMjAyNCBOZXcgVmVjdG9yIEx0ZC5cbkNvcHlyaWdodCAyMDIxLCAyMDIyIFRoZSBNYXRyaXgub3JnIEZvdW5kYXRpb24gQy5JLkMuXG5cblNQRFgtTGljZW5zZS1JZGVudGlmaWVyOiBBR1BMLTMuMC1vbmx5IE9SIEdQTC0zLjAtb25seVxuUGxlYXNlIHNlZSBMSUNFTlNFIGZpbGVzIGluIHRoZSByZXBvc2l0b3J5IHJvb3QgZm9yIGZ1bGwgZGV0YWlscy5cbiovXG5cbmltcG9ydCB7IE1hdHJpeEV2ZW50LCBSb29tLCBFdmVudFR5cGUgfSBmcm9tIFwibWF0cml4LWpzLXNkay9zcmMvbWF0cml4XCI7XG5pbXBvcnQgeyBsb2dnZXIgfSBmcm9tIFwibWF0cml4LWpzLXNkay9zcmMvbG9nZ2VyXCI7XG5cbmltcG9ydCB7IFBsYXliYWNrLCBQbGF5YmFja1N0YXRlIH0gZnJvbSBcIi4vUGxheWJhY2tcIjtcbmltcG9ydCB7IFVQREFURV9FVkVOVCB9IGZyb20gXCIuLi9zdG9yZXMvQXN5bmNTdG9yZVwiO1xuaW1wb3J0IHsgTWF0cml4Q2xpZW50UGVnIH0gZnJvbSBcIi4uL01hdHJpeENsaWVudFBlZ1wiO1xuaW1wb3J0IHsgYXJyYXlGYXN0Q2xvbmUgfSBmcm9tIFwiLi4vdXRpbHMvYXJyYXlzXCI7XG5pbXBvcnQgeyBQbGF5YmFja01hbmFnZXIgfSBmcm9tIFwiLi9QbGF5YmFja01hbmFnZXJcIjtcbmltcG9ydCB7IGlzVm9pY2VNZXNzYWdlIH0gZnJvbSBcIi4uL3V0aWxzL0V2ZW50VXRpbHNcIjtcbmltcG9ydCB7IFNka0NvbnRleHRDbGFzcyB9IGZyb20gXCIuLi9jb250ZXh0cy9TREtDb250ZXh0XCI7XG5cbi8qKlxuICogQXVkaW8gcGxheWJhY2sgcXVldWUgbWFuYWdlbWVudCBmb3IgYSBnaXZlbiByb29tLiBUaGlzIGtlZXBzIHRyYWNrIG9mIHdoZXJlIHRoZSB1c2VyXG4gKiB3YXMgYXQgZm9yIGVhY2ggcGxheWJhY2ssIHdoYXQgb3JkZXIgdGhlIHBsYXliYWNrcyB3ZXJlIHBsYXllZCBpbiwgYW5kIHRyaWdnZXJzIHN1YnNlcXVlbnRcbiAqIHBsYXliYWNrcy5cbiAqXG4gKiBDdXJyZW50bHkgdGhpcyBpcyBvbmx5IGludGVuZGVkIHRvIGJlIHVzZWQgYnkgdm9pY2UgbWVzc2FnZXMuXG4gKlxuICogVGhlIHByaW1hcnkgbWVjaGFuaWNzIGFyZTpcbiAqICogUGVyc2lzdGVkIGNsb2NrIHN0YXRlIGZvciBlYWNoIHBsYXliYWNrIGluc3RhbmNlICh0aWVkIHRvIEV2ZW50IElEKS5cbiAqICogTGltaXRlZCBtZW1vcnkgb2YgcGxheWJhY2sgb3JkZXIgKHNlZSBjb2RlOyBub3QgcGVyc2lzdGVkKS5cbiAqICogQXV0b3BsYXkgb2YgbmV4dCBlbGlnaWJsZSBwbGF5YmFjayBpbnN0YW5jZS5cbiAqL1xuZXhwb3J0IGNsYXNzIFBsYXliYWNrUXVldWUge1xuICAgIHByaXZhdGUgc3RhdGljIHF1ZXVlcyA9IG5ldyBNYXA8c3RyaW5nLCBQbGF5YmFja1F1ZXVlPigpOyAvLyBrZXllZCBieSByb29tIElEXG5cbiAgICBwcml2YXRlIHBsYXliYWNrcyA9IG5ldyBNYXA8c3RyaW5nLCBQbGF5YmFjaz4oKTsgLy8ga2V5ZWQgYnkgZXZlbnQgSURcbiAgICBwcml2YXRlIGNsb2NrU3RhdGVzID0gbmV3IE1hcDxzdHJpbmcsIG51bWJlcj4oKTsgLy8ga2V5ZWQgYnkgZXZlbnQgSURcbiAgICBwcml2YXRlIHBsYXliYWNrSWRPcmRlcjogc3RyaW5nW10gPSBbXTsgLy8gZXZlbnQgSURzLCBsYXN0ID09IGN1cnJlbnRcbiAgICBwcml2YXRlIGN1cnJlbnRQbGF5YmFja0lkOiBzdHJpbmcgfCBudWxsID0gbnVsbDsgLy8gZXZlbnQgSUQsIGJyb2tlbiBvdXQgZnJvbSBhYm92ZSBmb3IgZWFzZSBvZiB1c2VcbiAgICBwcml2YXRlIHJlY2VudEZ1bGxQbGF5cyA9IG5ldyBTZXQ8c3RyaW5nPigpOyAvLyBldmVudCBJRHNcblxuICAgIHB1YmxpYyBjb25zdHJ1Y3Rvcihwcml2YXRlIHJvb206IFJvb20pIHtcbiAgICAgICAgdGhpcy5sb2FkQ2xvY2tzKCk7XG5cbiAgICAgICAgU2RrQ29udGV4dENsYXNzLmluc3RhbmNlLnJvb21WaWV3U3RvcmUuYWRkUm9vbUxpc3RlbmVyKHRoaXMucm9vbS5yb29tSWQsIChpc0FjdGl2ZSkgPT4ge1xuICAgICAgICAgICAgaWYgKCFpc0FjdGl2ZSkgcmV0dXJuO1xuXG4gICAgICAgICAgICAvLyBSZXNldCB0aGUgc3RhdGUgb2YgdGhlIHBsYXliYWNrcyBiZWZvcmUgdGhleSBzdGFydCBtb3VudGluZyBhbmQgZW5xdWV1aW5nIHVwZGF0ZXMuXG4gICAgICAgICAgICAvLyBXZSByZXNldCB0aGUgZW50aXJldHkgb2YgdGhlIHF1ZXVlLCBpbmNsdWRpbmcgb3JkZXIsIHRvIGVuc3VyZSB0aGUgdXNlciBpc24ndCBsZWZ0XG4gICAgICAgICAgICAvLyBjb25mdXNlZCB3aXRoIHdoYXQgb3JkZXIgdGhlIG1lc3NhZ2VzIGFyZSBwbGF5aW5nIGluLlxuICAgICAgICAgICAgdGhpcy5jdXJyZW50UGxheWJhY2tJZCA9IG51bGw7IC8vIHRoaXMgaW4gcGFydGljdWxhciBzdG9wcyBhdXRvcGxheSB3aGVuIHRoZSByb29tIGlzIHN3aXRjaGVkIHRvXG4gICAgICAgICAgICB0aGlzLnJlY2VudEZ1bGxQbGF5cyA9IG5ldyBTZXQ8c3RyaW5nPigpO1xuICAgICAgICAgICAgdGhpcy5wbGF5YmFja0lkT3JkZXIgPSBbXTtcbiAgICAgICAgfSk7XG4gICAgfVxuXG4gICAgcHVibGljIHN0YXRpYyBmb3JSb29tKHJvb21JZDogc3RyaW5nKTogUGxheWJhY2tRdWV1ZSB7XG4gICAgICAgIGNvbnN0IGNsaSA9IE1hdHJpeENsaWVudFBlZy5zYWZlR2V0KCk7XG4gICAgICAgIGNvbnN0IHJvb20gPSBjbGkuZ2V0Um9vbShyb29tSWQpO1xuICAgICAgICBpZiAoIXJvb20pIHRocm93IG5ldyBFcnJvcihcIlVua25vd24gcm9vbVwiKTtcbiAgICAgICAgaWYgKFBsYXliYWNrUXVldWUucXVldWVzLmhhcyhyb29tLnJvb21JZCkpIHtcbiAgICAgICAgICAgIHJldHVybiBQbGF5YmFja1F1ZXVlLnF1ZXVlcy5nZXQocm9vbS5yb29tSWQpITtcbiAgICAgICAgfVxuICAgICAgICBjb25zdCBxdWV1ZSA9IG5ldyBQbGF5YmFja1F1ZXVlKHJvb20pO1xuICAgICAgICBQbGF5YmFja1F1ZXVlLnF1ZXVlcy5zZXQocm9vbS5yb29tSWQsIHF1ZXVlKTtcbiAgICAgICAgcmV0dXJuIHF1ZXVlO1xuICAgIH1cblxuICAgIHByaXZhdGUgcGVyc2lzdENsb2NrcygpOiB2b2lkIHtcbiAgICAgICAgbG9jYWxTdG9yYWdlLnNldEl0ZW0oXG4gICAgICAgICAgICBgbXhfdm9pY2VfbWVzc2FnZV9jbG9ja3NfJHt0aGlzLnJvb20ucm9vbUlkfWAsXG4gICAgICAgICAgICBKU09OLnN0cmluZ2lmeShBcnJheS5mcm9tKHRoaXMuY2xvY2tTdGF0ZXMuZW50cmllcygpKSksXG4gICAgICAgICk7XG4gICAgfVxuXG4gICAgcHJpdmF0ZSBsb2FkQ2xvY2tzKCk6IHZvaWQge1xuICAgICAgICBjb25zdCB2YWwgPSBsb2NhbFN0b3JhZ2UuZ2V0SXRlbShgbXhfdm9pY2VfbWVzc2FnZV9jbG9ja3NfJHt0aGlzLnJvb20ucm9vbUlkfWApO1xuICAgICAgICBpZiAoISF2YWwpIHtcbiAgICAgICAgICAgIHRoaXMuY2xvY2tTdGF0ZXMgPSBuZXcgTWFwPHN0cmluZywgbnVtYmVyPihKU09OLnBhcnNlKHZhbCkpO1xuICAgICAgICB9XG4gICAgfVxuXG4gICAgcHVibGljIHVuc29ydGVkRW5xdWV1ZShteEV2ZW50OiBNYXRyaXhFdmVudCwgcGxheWJhY2s6IFBsYXliYWNrKTogdm9pZCB7XG4gICAgICAgIC8vIFdlIGRvbid0IGV2ZXIgZGV0YWNoIG91ciBsaXN0ZW5lcnM6IHdlIGV4cGVjdCB0aGUgUGxheWJhY2sgdG8gY2xlYW4gdXAgZm9yIHVzXG4gICAgICAgIHRoaXMucGxheWJhY2tzLnNldChteEV2ZW50LmdldElkKCkhLCBwbGF5YmFjayk7XG4gICAgICAgIHBsYXliYWNrLm9uKFVQREFURV9FVkVOVCwgKHN0YXRlKSA9PiB0aGlzLm9uUGxheWJhY2tTdGF0ZUNoYW5nZShwbGF5YmFjaywgbXhFdmVudCwgc3RhdGUpKTtcbiAgICAgICAgcGxheWJhY2suY2xvY2tJbmZvLmxpdmVEYXRhLm9uVXBkYXRlKChjbG9jaykgPT4gdGhpcy5vblBsYXliYWNrQ2xvY2socGxheWJhY2ssIG14RXZlbnQsIGNsb2NrKSk7XG4gICAgfVxuXG4gICAgcHJpdmF0ZSBvblBsYXliYWNrU3RhdGVDaGFuZ2UocGxheWJhY2s6IFBsYXliYWNrLCBteEV2ZW50OiBNYXRyaXhFdmVudCwgbmV3U3RhdGU6IFBsYXliYWNrU3RhdGUpOiB2b2lkIHtcbiAgICAgICAgLy8gUmVtZW1iZXIgd2hlcmUgdGhlIHVzZXIgZ290IHRvIGluIHBsYXliYWNrXG4gICAgICAgIGNvbnN0IHdhc0xhc3RQbGF5aW5nID0gdGhpcy5jdXJyZW50UGxheWJhY2tJZCA9PT0gbXhFdmVudC5nZXRJZCgpO1xuICAgICAgICBpZiAobmV3U3RhdGUgPT09IFBsYXliYWNrU3RhdGUuU3RvcHBlZCAmJiB0aGlzLmNsb2NrU3RhdGVzLmhhcyhteEV2ZW50LmdldElkKCkhKSAmJiAhd2FzTGFzdFBsYXlpbmcpIHtcbiAgICAgICAgICAgIC8vIG5vaW5zcGVjdGlvbiBKU0lnbm9yZWRQcm9taXNlRnJvbUNhbGxcbiAgICAgICAgICAgIHBsYXliYWNrLnNraXBUbyh0aGlzLmNsb2NrU3RhdGVzLmdldChteEV2ZW50LmdldElkKCkhKSEpO1xuICAgICAgICB9IGVsc2UgaWYgKG5ld1N0YXRlID09PSBQbGF5YmFja1N0YXRlLlN0b3BwZWQpIHtcbiAgICAgICAgICAgIC8vIFJlbW92ZSB0aGUgbm93LXVzZWxlc3MgY2xvY2sgZm9yIHNvbWUgc3BhY2Ugc2F2aW5nc1xuICAgICAgICAgICAgdGhpcy5jbG9ja1N0YXRlcy5kZWxldGUobXhFdmVudC5nZXRJZCgpISk7XG5cbiAgICAgICAgICAgIGlmICh3YXNMYXN0UGxheWluZyAmJiB0aGlzLmN1cnJlbnRQbGF5YmFja0lkKSB7XG4gICAgICAgICAgICAgICAgdGhpcy5yZWNlbnRGdWxsUGxheXMuYWRkKHRoaXMuY3VycmVudFBsYXliYWNrSWQpO1xuICAgICAgICAgICAgICAgIGNvbnN0IG9yZGVyQ2xvbmUgPSBhcnJheUZhc3RDbG9uZSh0aGlzLnBsYXliYWNrSWRPcmRlcik7XG4gICAgICAgICAgICAgICAgY29uc3QgbGFzdCA9IG9yZGVyQ2xvbmUucG9wKCk7XG4gICAgICAgICAgICAgICAgaWYgKGxhc3QgPT09IHRoaXMuY3VycmVudFBsYXliYWNrSWQpIHtcbiAgICAgICAgICAgICAgICAgICAgY29uc3QgbmV4dCA9IG9yZGVyQ2xvbmUucG9wKCk7XG4gICAgICAgICAgICAgICAgICAgIGlmIChuZXh0KSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBjb25zdCBpbnN0YW5jZSA9IHRoaXMucGxheWJhY2tzLmdldChuZXh0KTtcbiAgICAgICAgICAgICAgICAgICAgICAgIGlmICghaW5zdGFuY2UpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBsb2dnZXIud2FybihcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgXCJWb2ljZSBtZXNzYWdlIHF1ZXVlIGRlc3luYzogTWlzc2luZyBwbGF5YmFjayBmb3IgbmV4dCBtZXNzYWdlOiBcIiArXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBgQ3VycmVudD0ke3RoaXMuY3VycmVudFBsYXliYWNrSWR9IExhc3Q9JHtsYXN0fSBOZXh0PSR7bmV4dH1gLFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICk7XG4gICAgICAgICAgICAgICAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMucGxheWJhY2tJZE9yZGVyID0gb3JkZXJDbG9uZTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBQbGF5YmFja01hbmFnZXIuaW5zdGFuY2UucGF1c2VBbGxFeGNlcHQoaW5zdGFuY2UpO1xuXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gVGhpcyBzaG91bGQgY2F1c2UgYSBQbGF5IGV2ZW50LCB3aGljaCB3aWxsIHJlLXBvcHVsYXRlIG91ciBwbGF5YmFjayBvcmRlclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIGFuZCB1cGRhdGUgb3VyIGN1cnJlbnQgcGxheWJhY2sgSUQuXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gbm9pbnNwZWN0aW9uIEpTSWdub3JlZFByb21pc2VGcm9tQ2FsbFxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGluc3RhbmNlLnBsYXkoKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgICAgIC8vIGVsc2Ugbm8gZXhwbGljaXQgbmV4dCBldmVudCwgc28gZmluZCBhbiBldmVudCB3ZSBoYXZlbid0IHBsYXllZCB0aGF0IGNvbWVzIG5leHQuIFRoZSBsaXZlXG4gICAgICAgICAgICAgICAgICAgICAgICAvLyB0aW1lbGluZSBpcyBhbHJlYWR5IG1vc3QgcmVjZW50IGxhc3QsIHNvIHdlIGNhbiBpdGVyYXRlIGRvd24gdGhhdC5cbiAgICAgICAgICAgICAgICAgICAgICAgIGNvbnN0IHRpbWVsaW5lID0gYXJyYXlGYXN0Q2xvbmUodGhpcy5yb29tLmdldExpdmVUaW1lbGluZSgpLmdldEV2ZW50cygpKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIGxldCBzY2FuRm9yVm9pY2VNZXNzYWdlID0gZmFsc2U7XG4gICAgICAgICAgICAgICAgICAgICAgICBsZXQgbmV4dEV2OiBNYXRyaXhFdmVudCB8IHVuZGVmaW5lZDtcbiAgICAgICAgICAgICAgICAgICAgICAgIGZvciAoY29uc3QgZXZlbnQgb2YgdGltZWxpbmUpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZiAoZXZlbnQuZ2V0SWQoKSA9PT0gbXhFdmVudC5nZXRJZCgpKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNjYW5Gb3JWb2ljZU1lc3NhZ2UgPSB0cnVlO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb250aW51ZTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYgKCFzY2FuRm9yVm9pY2VNZXNzYWdlKSBjb250aW51ZTtcblxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmICghaXNWb2ljZU1lc3NhZ2UoZXZlbnQpKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbnN0IGV2VHlwZSA9IGV2ZW50LmdldFR5cGUoKTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYgKGV2VHlwZSAhPT0gRXZlbnRUeXBlLlJvb21NZXNzYWdlICYmIGV2VHlwZSAhPT0gRXZlbnRUeXBlLlN0aWNrZXIpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbnRpbnVlOyAvLyBFdmVudCBjYW4gYmUgc2tpcHBlZCBmb3IgYXV0b21hdGljIHBsYXliYWNrIGNvbnNpZGVyYXRpb25cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBicmVhazsgLy8gU3RvcCBhdXRvbWF0aWMgcGxheWJhY2s6IG5leHQgdXNlZnVsIGV2ZW50IGlzIG5vdCBhIHZvaWNlIG1lc3NhZ2VcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB9XG5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb25zdCBoYXZlUGxheWJhY2sgPSB0aGlzLnBsYXliYWNrcy5oYXMoZXZlbnQuZ2V0SWQoKSEpO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbnN0IGlzUmVjZW50bHlDb21wbGV0ZWQgPSB0aGlzLnJlY2VudEZ1bGxQbGF5cy5oYXMoZXZlbnQuZ2V0SWQoKSEpO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmIChoYXZlUGxheWJhY2sgJiYgIWlzUmVjZW50bHlDb21wbGV0ZWQpIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmV4dEV2ID0gZXZlbnQ7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgICAgIGlmICghbmV4dEV2KSB7XG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gaWYgd2UgZG9uJ3QgaGF2ZSBhbnl3aGVyZSB0byBnbywgcmVzZXQgdGhlIHJlY2VudCBwbGF5YmFjayBxdWV1ZSBzbyB0aGUgdXNlclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIGNhbiBzdGFydCBhIG5ldyBjaGFpbiBvZiBwbGF5YmFja3MuXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy5yZWNlbnRGdWxsUGxheXMgPSBuZXcgU2V0PHN0cmluZz4oKTtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aGlzLnBsYXliYWNrSWRPcmRlciA9IFtdO1xuICAgICAgICAgICAgICAgICAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aGlzLnBsYXliYWNrSWRPcmRlciA9IG9yZGVyQ2xvbmU7XG5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb25zdCBpbnN0YW5jZSA9IHRoaXMucGxheWJhY2tzLmdldChuZXh0RXYuZ2V0SWQoKSEpO1xuICAgICAgICAgICAgICAgICAgICAgICAgICAgIFBsYXliYWNrTWFuYWdlci5pbnN0YW5jZS5wYXVzZUFsbEV4Y2VwdChpbnN0YW5jZSk7XG5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBUaGlzIHNob3VsZCBjYXVzZSBhIFBsYXkgZXZlbnQsIHdoaWNoIHdpbGwgcmUtcG9wdWxhdGUgb3VyIHBsYXliYWNrIG9yZGVyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgLy8gYW5kIHVwZGF0ZSBvdXIgY3VycmVudCBwbGF5YmFjayBJRC5cbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAvLyBub2luc3BlY3Rpb24gSlNJZ25vcmVkUHJvbWlzZUZyb21DYWxsXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgaW5zdGFuY2U/LnBsYXkoKTtcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICAgICAgICAgIGxvZ2dlci53YXJuKFxuICAgICAgICAgICAgICAgICAgICAgICAgXCJWb2ljZSBtZXNzYWdlIHF1ZXVlIGRlc3luYzogRXhwZWN0ZWQgcGxheWJhY2sgc3RvcCB0byBiZSBsYXN0IGluIG9yZGVyLiBcIiArXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgYEN1cnJlbnQ9JHt0aGlzLmN1cnJlbnRQbGF5YmFja0lkfSBMYXN0PSR7bGFzdH0gRXZlbnRJRD0ke214RXZlbnQuZ2V0SWQoKX1gLFxuICAgICAgICAgICAgICAgICAgICApO1xuICAgICAgICAgICAgICAgIH1cbiAgICAgICAgICAgIH1cbiAgICAgICAgfVxuXG4gICAgICAgIGlmIChuZXdTdGF0ZSA9PT0gUGxheWJhY2tTdGF0ZS5QbGF5aW5nKSB7XG4gICAgICAgICAgICBjb25zdCBvcmRlciA9IHRoaXMucGxheWJhY2tJZE9yZGVyO1xuICAgICAgICAgICAgaWYgKHRoaXMuY3VycmVudFBsYXliYWNrSWQgIT09IG14RXZlbnQuZ2V0SWQoKSAmJiAhIXRoaXMuY3VycmVudFBsYXliYWNrSWQpIHtcbiAgICAgICAgICAgICAgICBpZiAob3JkZXIubGVuZ3RoID09PSAwIHx8IG9yZGVyW29yZGVyLmxlbmd0aCAtIDFdICE9PSB0aGlzLmN1cnJlbnRQbGF5YmFja0lkKSB7XG4gICAgICAgICAgICAgICAgICAgIGNvbnN0IGxhc3RJbnN0YW5jZSA9IHRoaXMucGxheWJhY2tzLmdldCh0aGlzLmN1cnJlbnRQbGF5YmFja0lkKTtcbiAgICAgICAgICAgICAgICAgICAgaWYgKFxuICAgICAgICAgICAgICAgICAgICAgICAgbGFzdEluc3RhbmNlICYmXG4gICAgICAgICAgICAgICAgICAgICAgICBbUGxheWJhY2tTdGF0ZS5QbGF5aW5nLCBQbGF5YmFja1N0YXRlLlBhdXNlZF0uaW5jbHVkZXMobGFzdEluc3RhbmNlLmN1cnJlbnRTdGF0ZSlcbiAgICAgICAgICAgICAgICAgICAgKSB7XG4gICAgICAgICAgICAgICAgICAgICAgICBvcmRlci5wdXNoKHRoaXMuY3VycmVudFBsYXliYWNrSWQpO1xuICAgICAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgICAgfVxuICAgICAgICAgICAgfVxuXG4gICAgICAgICAgICB0aGlzLmN1cnJlbnRQbGF5YmFja0lkID0gbXhFdmVudC5nZXRJZCgpITtcbiAgICAgICAgICAgIGlmIChvcmRlci5sZW5ndGggPT09IDAgfHwgb3JkZXJbb3JkZXIubGVuZ3RoIC0gMV0gIT09IHRoaXMuY3VycmVudFBsYXliYWNrSWQpIHtcbiAgICAgICAgICAgICAgICBvcmRlci5wdXNoKHRoaXMuY3VycmVudFBsYXliYWNrSWQpO1xuICAgICAgICAgICAgfVxuICAgICAgICB9XG5cbiAgICAgICAgLy8gT25seSBwZXJzaXN0IGNsb2NrIGluZm9ybWF0aW9uIG9uIHBhdXNlL3N0b3AgKGVuZCkgdG8gYXZvaWQgb3ZlcndoZWxtaW5nIHRoZSBzdG9yYWdlLlxuICAgICAgICAvLyBUaGlzIHNob3VsZCBnZXQgdHJpZ2dlcmVkIGZyb20gbm9ybWFsIHZvaWNlIG1lc3NhZ2UgY29tcG9uZW50IHVubW91bnQgZHVlIHRvIHRoZSBwbGF5YmFja1xuICAgICAgICAvLyBzdG9wcGluZyBpdHNlbGYgZm9yIGNsZWFudXAuXG4gICAgICAgIGlmIChuZXdTdGF0ZSA9PT0gUGxheWJhY2tTdGF0ZS5QYXVzZWQgfHwgbmV3U3RhdGUgPT09IFBsYXliYWNrU3RhdGUuU3RvcHBlZCkge1xuICAgICAgICAgICAgdGhpcy5wZXJzaXN0Q2xvY2tzKCk7XG4gICAgICAgIH1cbiAgICB9XG5cbiAgICBwcml2YXRlIG9uUGxheWJhY2tDbG9jayhwbGF5YmFjazogUGxheWJhY2ssIG14RXZlbnQ6IE1hdHJpeEV2ZW50LCBjbG9ja3M6IG51bWJlcltdKTogdm9pZCB7XG4gICAgICAgIGlmIChwbGF5YmFjay5jdXJyZW50U3RhdGUgPT09IFBsYXliYWNrU3RhdGUuRGVjb2RpbmcpIHJldHVybjsgLy8gaWdub3JlIHByZS1yZWFkeSB2YWx1ZXNcblxuICAgICAgICBpZiAocGxheWJhY2suY3VycmVudFN0YXRlICE9PSBQbGF5YmFja1N0YXRlLlN0b3BwZWQpIHtcbiAgICAgICAgICAgIHRoaXMuY2xvY2tTdGF0ZXMuc2V0KG14RXZlbnQuZ2V0SWQoKSEsIGNsb2Nrc1swXSk7IC8vIFswXSBpcyB0aGUgY3VycmVudCBzZWVrIHBvc2l0aW9uXG4gICAgICAgIH1cbiAgICB9XG59XG4iXSwibWFwcGluZ3MiOiI7Ozs7Ozs7O0FBUUEsSUFBQUEsT0FBQSxHQUFBQyxPQUFBO0FBQ0EsSUFBQUMsT0FBQSxHQUFBRCxPQUFBO0FBRUEsSUFBQUUsU0FBQSxHQUFBRixPQUFBO0FBQ0EsSUFBQUcsV0FBQSxHQUFBSCxPQUFBO0FBQ0EsSUFBQUksZ0JBQUEsR0FBQUosT0FBQTtBQUNBLElBQUFLLE9BQUEsR0FBQUwsT0FBQTtBQUNBLElBQUFNLGdCQUFBLEdBQUFOLE9BQUE7QUFDQSxJQUFBTyxXQUFBLEdBQUFQLE9BQUE7QUFDQSxJQUFBUSxXQUFBLEdBQUFSLE9BQUE7QUFBeUQsSUFBQVMsY0FBQTtBQWpCekQ7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFhQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDTyxNQUFNQyxhQUFhLENBQUM7RUFPc0I7O0VBRXRDQyxXQUFXQSxDQUFTQyxJQUFVLEVBQUU7SUFSbUI7SUFBQSxJQUFBQyxnQkFBQSxDQUFBQyxPQUFBLHFCQUV0QyxJQUFJQyxHQUFHLENBQW1CLENBQUM7SUFBRTtJQUFBLElBQUFGLGdCQUFBLENBQUFDLE9BQUEsdUJBQzNCLElBQUlDLEdBQUcsQ0FBaUIsQ0FBQztJQUFFO0lBQUEsSUFBQUYsZ0JBQUEsQ0FBQUMsT0FBQSwyQkFDYixFQUFFO0lBQUU7SUFBQSxJQUFBRCxnQkFBQSxDQUFBQyxPQUFBLDZCQUNHLElBQUk7SUFBRTtJQUFBLElBQUFELGdCQUFBLENBQUFDLE9BQUEsMkJBQ3ZCLElBQUlFLEdBQUcsQ0FBUyxDQUFDO0lBQUEsS0FFaEJKLElBQVUsR0FBVkEsSUFBVTtJQUNqQyxJQUFJLENBQUNLLFVBQVUsQ0FBQyxDQUFDO0lBRWpCQywyQkFB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