mirror of
https://github.com/DanielnetoDotCom/YouPHPTube
synced 2025-10-03 17:59:55 +02:00
add p2p support for HLS https://github.com/Novage/p2p-media-loader
This commit is contained in:
parent
64c36d9f4e
commit
0d0338876d
1197 changed files with 121461 additions and 179724 deletions
308
node_modules/@videojs/http-streaming/dist/videojs-http-streaming-sync-workers.js
generated
vendored
308
node_modules/@videojs/http-streaming/dist/videojs-http-streaming-sync-workers.js
generated
vendored
|
@ -1,4 +1,4 @@
|
|||
/*! @name @videojs/http-streaming @version 2.14.3 @license Apache-2.0 */
|
||||
/*! @name @videojs/http-streaming @version 2.16.0 @license Apache-2.0 */
|
||||
(function (global, factory) {
|
||||
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('video.js'), require('@xmldom/xmldom')) :
|
||||
typeof define === 'function' && define.amd ? define(['exports', 'video.js', '@xmldom/xmldom'], factory) :
|
||||
|
@ -470,7 +470,7 @@
|
|||
return array;
|
||||
}
|
||||
|
||||
/*! @name m3u8-parser @version 4.7.1 @license Apache-2.0 */
|
||||
/*! @name m3u8-parser @version 4.8.0 @license Apache-2.0 */
|
||||
/**
|
||||
* A stream that buffers string input and generates a `data` event for each
|
||||
* line.
|
||||
|
@ -877,6 +877,10 @@
|
|||
event.attributes.BANDWIDTH = parseInt(event.attributes.BANDWIDTH, 10);
|
||||
}
|
||||
|
||||
if (event.attributes['FRAME-RATE']) {
|
||||
event.attributes['FRAME-RATE'] = parseFloat(event.attributes['FRAME-RATE']);
|
||||
}
|
||||
|
||||
if (event.attributes['PROGRAM-ID']) {
|
||||
event.attributes['PROGRAM-ID'] = parseInt(event.attributes['PROGRAM-ID'], 10);
|
||||
}
|
||||
|
@ -5471,7 +5475,7 @@
|
|||
});
|
||||
};
|
||||
|
||||
/*! @name mpd-parser @version 0.21.1 @license Apache-2.0 */
|
||||
/*! @name mpd-parser @version 0.22.1 @license Apache-2.0 */
|
||||
|
||||
var isObject = function isObject(obj) {
|
||||
return !!obj && typeof obj === 'object';
|
||||
|
@ -6562,6 +6566,10 @@
|
|||
segments: segments
|
||||
};
|
||||
|
||||
if (attributes.frameRate) {
|
||||
playlist.attributes['FRAME-RATE'] = attributes.frameRate;
|
||||
}
|
||||
|
||||
if (attributes.contentProtection) {
|
||||
playlist.contentProtection = attributes.contentProtection;
|
||||
}
|
||||
|
@ -7219,6 +7227,20 @@
|
|||
var getContent = function getContent(element) {
|
||||
return element.textContent.trim();
|
||||
};
|
||||
/**
|
||||
* Converts the provided string that may contain a division operation to a number.
|
||||
*
|
||||
* @param {string} value - the provided string value
|
||||
*
|
||||
* @return {number} the parsed string value
|
||||
*/
|
||||
|
||||
|
||||
var parseDivisionValue = function parseDivisionValue(value) {
|
||||
return parseFloat(value.split('/').reduce(function (prev, current) {
|
||||
return prev / current;
|
||||
}));
|
||||
};
|
||||
|
||||
var parseDuration = function parseDuration(str) {
|
||||
var SECONDS_IN_YEAR = 365 * 24 * 60 * 60;
|
||||
|
@ -7387,6 +7409,18 @@
|
|||
return parseInt(value, 10);
|
||||
},
|
||||
|
||||
/**
|
||||
* Specifies the frame rate of the representation
|
||||
*
|
||||
* @param {string} value
|
||||
* value of attribute as a string
|
||||
* @return {number}
|
||||
* The parsed frame rate
|
||||
*/
|
||||
frameRate: function frameRate(value) {
|
||||
return parseDivisionValue(value);
|
||||
},
|
||||
|
||||
/**
|
||||
* Specifies the number of the first Media Segment in this Representation in the Period
|
||||
*
|
||||
|
@ -9928,7 +9962,7 @@
|
|||
return fn;
|
||||
};
|
||||
|
||||
/* rollup-plugin-worker-factory start for worker!/Users/abarstow/videojs/http-streaming/src/transmuxer-worker.js */
|
||||
/* rollup-plugin-worker-factory start for worker!/Users/ddashkevich/projects/vhs-release/src/transmuxer-worker.js */
|
||||
var workerCode$1 = transform(function (self) {
|
||||
/**
|
||||
* mux.js
|
||||
|
@ -18731,7 +18765,7 @@
|
|||
};
|
||||
});
|
||||
var TransmuxWorker = factory(workerCode$1);
|
||||
/* rollup-plugin-worker-factory end for worker!/Users/abarstow/videojs/http-streaming/src/transmuxer-worker.js */
|
||||
/* rollup-plugin-worker-factory end for worker!/Users/ddashkevich/projects/vhs-release/src/transmuxer-worker.js */
|
||||
|
||||
var handleData_ = function handleData_(event, transmuxedData, callback) {
|
||||
var _event$data$segment = event.data.segment,
|
||||
|
@ -23823,6 +23857,7 @@
|
|||
this.bandwidth = 1;
|
||||
this.roundTrip = NaN;
|
||||
this.trigger('bandwidthupdate');
|
||||
this.trigger('timeout');
|
||||
}
|
||||
/**
|
||||
* Handle the callback from the segmentRequest function and set the
|
||||
|
@ -25315,6 +25350,111 @@
|
|||
return SourceUpdater;
|
||||
}(videojs__default["default"].EventTarget);
|
||||
|
||||
var getPrototypeOf = createCommonjsModule(function (module) {
|
||||
function _getPrototypeOf(o) {
|
||||
module.exports = _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) {
|
||||
return o.__proto__ || Object.getPrototypeOf(o);
|
||||
};
|
||||
module.exports["default"] = module.exports, module.exports.__esModule = true;
|
||||
return _getPrototypeOf(o);
|
||||
}
|
||||
|
||||
module.exports = _getPrototypeOf;
|
||||
module.exports["default"] = module.exports, module.exports.__esModule = true;
|
||||
});
|
||||
|
||||
var isNativeFunction = createCommonjsModule(function (module) {
|
||||
function _isNativeFunction(fn) {
|
||||
return Function.toString.call(fn).indexOf("[native code]") !== -1;
|
||||
}
|
||||
|
||||
module.exports = _isNativeFunction;
|
||||
module.exports["default"] = module.exports, module.exports.__esModule = true;
|
||||
});
|
||||
|
||||
var isNativeReflectConstruct = createCommonjsModule(function (module) {
|
||||
function _isNativeReflectConstruct() {
|
||||
if (typeof Reflect === "undefined" || !Reflect.construct) return false;
|
||||
if (Reflect.construct.sham) return false;
|
||||
if (typeof Proxy === "function") return true;
|
||||
|
||||
try {
|
||||
Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {}));
|
||||
return true;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = _isNativeReflectConstruct;
|
||||
module.exports["default"] = module.exports, module.exports.__esModule = true;
|
||||
});
|
||||
|
||||
var construct = createCommonjsModule(function (module) {
|
||||
function _construct(Parent, args, Class) {
|
||||
if (isNativeReflectConstruct()) {
|
||||
module.exports = _construct = Reflect.construct;
|
||||
module.exports["default"] = module.exports, module.exports.__esModule = true;
|
||||
} else {
|
||||
module.exports = _construct = function _construct(Parent, args, Class) {
|
||||
var a = [null];
|
||||
a.push.apply(a, args);
|
||||
var Constructor = Function.bind.apply(Parent, a);
|
||||
var instance = new Constructor();
|
||||
if (Class) setPrototypeOf(instance, Class.prototype);
|
||||
return instance;
|
||||
};
|
||||
|
||||
module.exports["default"] = module.exports, module.exports.__esModule = true;
|
||||
}
|
||||
|
||||
return _construct.apply(null, arguments);
|
||||
}
|
||||
|
||||
module.exports = _construct;
|
||||
module.exports["default"] = module.exports, module.exports.__esModule = true;
|
||||
});
|
||||
|
||||
var wrapNativeSuper = createCommonjsModule(function (module) {
|
||||
function _wrapNativeSuper(Class) {
|
||||
var _cache = typeof Map === "function" ? new Map() : undefined;
|
||||
|
||||
module.exports = _wrapNativeSuper = function _wrapNativeSuper(Class) {
|
||||
if (Class === null || !isNativeFunction(Class)) return Class;
|
||||
|
||||
if (typeof Class !== "function") {
|
||||
throw new TypeError("Super expression must either be null or a function");
|
||||
}
|
||||
|
||||
if (typeof _cache !== "undefined") {
|
||||
if (_cache.has(Class)) return _cache.get(Class);
|
||||
|
||||
_cache.set(Class, Wrapper);
|
||||
}
|
||||
|
||||
function Wrapper() {
|
||||
return construct(Class, arguments, getPrototypeOf(this).constructor);
|
||||
}
|
||||
|
||||
Wrapper.prototype = Object.create(Class.prototype, {
|
||||
constructor: {
|
||||
value: Wrapper,
|
||||
enumerable: false,
|
||||
writable: true,
|
||||
configurable: true
|
||||
}
|
||||
});
|
||||
return setPrototypeOf(Wrapper, Class);
|
||||
};
|
||||
|
||||
module.exports["default"] = module.exports, module.exports.__esModule = true;
|
||||
return _wrapNativeSuper(Class);
|
||||
}
|
||||
|
||||
module.exports = _wrapNativeSuper;
|
||||
module.exports["default"] = module.exports, module.exports.__esModule = true;
|
||||
});
|
||||
|
||||
var uint8ToUtf8 = function uint8ToUtf8(uintArray) {
|
||||
return decodeURIComponent(escape(String.fromCharCode.apply(null, uintArray)));
|
||||
};
|
||||
|
@ -25322,6 +25462,16 @@
|
|||
var VTT_LINE_TERMINATORS = new Uint8Array('\n\n'.split('').map(function (char) {
|
||||
return char.charCodeAt(0);
|
||||
}));
|
||||
|
||||
var NoVttJsError = /*#__PURE__*/function (_Error) {
|
||||
inheritsLoose(NoVttJsError, _Error);
|
||||
|
||||
function NoVttJsError() {
|
||||
return _Error.call(this, 'Trying to parse received VTT cues, but there is no WebVTT. Make sure vtt.js is loaded.') || this;
|
||||
}
|
||||
|
||||
return NoVttJsError;
|
||||
}( /*#__PURE__*/wrapNativeSuper(Error));
|
||||
/**
|
||||
* An object that manages segment loading and appending.
|
||||
*
|
||||
|
@ -25330,6 +25480,7 @@
|
|||
* @extends videojs.EventTarget
|
||||
*/
|
||||
|
||||
|
||||
var VTTSegmentLoader = /*#__PURE__*/function (_SegmentLoader) {
|
||||
inheritsLoose(VTTSegmentLoader, _SegmentLoader);
|
||||
|
||||
|
@ -25346,7 +25497,8 @@
|
|||
_this.mediaSource_ = null;
|
||||
_this.subtitlesTrack_ = null;
|
||||
_this.loaderType_ = 'subtitle';
|
||||
_this.featuresNativeTextTracks_ = settings.featuresNativeTextTracks; // The VTT segment will have its own time mappings. Saving VTT segment timing info in
|
||||
_this.featuresNativeTextTracks_ = settings.featuresNativeTextTracks;
|
||||
_this.loadVttJs = settings.loadVttJs; // The VTT segment will have its own time mappings. Saving VTT segment timing info in
|
||||
// the sync controller leads to improper behavior.
|
||||
|
||||
_this.shouldSaveSegmentTimingInfo_ = false;
|
||||
|
@ -25621,30 +25773,19 @@
|
|||
segment.map.bytes = simpleSegment.map.bytes;
|
||||
}
|
||||
|
||||
segmentInfo.bytes = simpleSegment.bytes; // Make sure that vttjs has loaded, otherwise, wait till it finished loading
|
||||
segmentInfo.bytes = simpleSegment.bytes; // Make sure that vttjs has loaded, otherwise, load it and wait till it finished loading
|
||||
|
||||
if (typeof window.WebVTT !== 'function' && this.subtitlesTrack_ && this.subtitlesTrack_.tech_) {
|
||||
var loadHandler;
|
||||
if (typeof window.WebVTT !== 'function' && typeof this.loadVttJs === 'function') {
|
||||
this.state = 'WAITING_ON_VTTJS'; // should be fine to call multiple times
|
||||
// script will be loaded once but multiple listeners will be added to the queue, which is expected.
|
||||
|
||||
var errorHandler = function errorHandler() {
|
||||
_this3.subtitlesTrack_.tech_.off('vttjsloaded', loadHandler);
|
||||
|
||||
_this3.stopForError({
|
||||
this.loadVttJs().then(function () {
|
||||
return _this3.segmentRequestFinished_(error, simpleSegment, result);
|
||||
}, function () {
|
||||
return _this3.stopForError({
|
||||
message: 'Error loading vtt.js'
|
||||
});
|
||||
|
||||
return;
|
||||
};
|
||||
|
||||
loadHandler = function loadHandler() {
|
||||
_this3.subtitlesTrack_.tech_.off('vttjserror', errorHandler);
|
||||
|
||||
_this3.segmentRequestFinished_(error, simpleSegment, result);
|
||||
};
|
||||
|
||||
this.state = 'WAITING_ON_VTTJS';
|
||||
this.subtitlesTrack_.tech_.one('vttjsloaded', loadHandler);
|
||||
this.subtitlesTrack_.tech_.one('vttjserror', errorHandler);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -25704,6 +25845,8 @@
|
|||
/**
|
||||
* Uses the WebVTT parser to parse the segment response
|
||||
*
|
||||
* @throws NoVttJsError
|
||||
*
|
||||
* @param {Object} segmentInfo
|
||||
* a segment info object that describes the current segment
|
||||
* @private
|
||||
|
@ -25714,6 +25857,11 @@
|
|||
var decoder;
|
||||
var decodeBytesToString = false;
|
||||
|
||||
if (typeof window.WebVTT !== 'function') {
|
||||
// caller is responsible for exception handling.
|
||||
throw new NoVttJsError();
|
||||
}
|
||||
|
||||
if (typeof window.TextDecoder === 'function') {
|
||||
decoder = new window.TextDecoder('utf8');
|
||||
} else {
|
||||
|
@ -26550,7 +26698,7 @@
|
|||
return TimelineChangeController;
|
||||
}(videojs__default["default"].EventTarget);
|
||||
|
||||
/* rollup-plugin-worker-factory start for worker!/Users/abarstow/videojs/http-streaming/src/decrypter-worker.js */
|
||||
/* rollup-plugin-worker-factory start for worker!/Users/ddashkevich/projects/vhs-release/src/decrypter-worker.js */
|
||||
var workerCode = transform(function (self) {
|
||||
|
||||
var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
|
||||
|
@ -27267,7 +27415,7 @@
|
|||
};
|
||||
});
|
||||
var Decrypter = factory(workerCode);
|
||||
/* rollup-plugin-worker-factory end for worker!/Users/abarstow/videojs/http-streaming/src/decrypter-worker.js */
|
||||
/* rollup-plugin-worker-factory end for worker!/Users/ddashkevich/projects/vhs-release/src/decrypter-worker.js */
|
||||
|
||||
/**
|
||||
* Convert the properties of an HLS track into an audioTrackKind.
|
||||
|
@ -28396,7 +28544,25 @@
|
|||
}), options);
|
||||
_this.subtitleSegmentLoader_ = new VTTSegmentLoader(videojs__default["default"].mergeOptions(segmentLoaderSettings, {
|
||||
loaderType: 'vtt',
|
||||
featuresNativeTextTracks: _this.tech_.featuresNativeTextTracks
|
||||
featuresNativeTextTracks: _this.tech_.featuresNativeTextTracks,
|
||||
loadVttJs: function loadVttJs() {
|
||||
return new Promise(function (resolve, reject) {
|
||||
function onLoad() {
|
||||
tech.off('vttjserror', onError);
|
||||
resolve();
|
||||
}
|
||||
|
||||
function onError() {
|
||||
tech.off('vttjsloaded', onLoad);
|
||||
reject();
|
||||
}
|
||||
|
||||
tech.one('vttjsloaded', onLoad);
|
||||
tech.one('vttjserror', onError); // safe to call multiple times, script will be loaded only once:
|
||||
|
||||
tech.addWebVttScript_();
|
||||
});
|
||||
}
|
||||
}), options);
|
||||
|
||||
_this.setupSegmentLoaderListeners_();
|
||||
|
@ -28486,16 +28652,20 @@
|
|||
/**
|
||||
* Run selectPlaylist and switch to the new playlist if we should
|
||||
*
|
||||
* @param {string} [reason=abr] a reason for why the ABR check is made
|
||||
* @private
|
||||
*
|
||||
*/
|
||||
;
|
||||
|
||||
_proto.checkABR_ = function checkABR_() {
|
||||
_proto.checkABR_ = function checkABR_(reason) {
|
||||
if (reason === void 0) {
|
||||
reason = 'abr';
|
||||
}
|
||||
|
||||
var nextPlaylist = this.selectPlaylist();
|
||||
|
||||
if (nextPlaylist && this.shouldSwitchToMedia_(nextPlaylist)) {
|
||||
this.switchMedia_(nextPlaylist, 'abr');
|
||||
this.switchMedia_(nextPlaylist, reason);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -28747,7 +28917,9 @@
|
|||
_this3.requestOptions_.timeout = 0;
|
||||
} else {
|
||||
_this3.requestOptions_.timeout = requestTimeout;
|
||||
} // TODO: Create a new event on the PlaylistLoader that signals
|
||||
}
|
||||
|
||||
_this3.masterPlaylistLoader_.load(); // TODO: Create a new event on the PlaylistLoader that signals
|
||||
// that the segments have changed in some way and use that to
|
||||
// update the SegmentLoader instead of doing it twice here and
|
||||
// on `loadedplaylist`
|
||||
|
@ -28951,16 +29123,25 @@
|
|||
_proto.setupSegmentLoaderListeners_ = function setupSegmentLoaderListeners_() {
|
||||
var _this4 = this;
|
||||
|
||||
this.mainSegmentLoader_.on('bandwidthupdate', function () {
|
||||
// Whether or not buffer based ABR or another ABR is used, on a bandwidth change it's
|
||||
// useful to check to see if a rendition switch should be made.
|
||||
_this4.checkABR_('bandwidthupdate');
|
||||
|
||||
_this4.tech_.trigger('bandwidthupdate');
|
||||
});
|
||||
this.mainSegmentLoader_.on('timeout', function () {
|
||||
if (_this4.experimentalBufferBasedABR) {
|
||||
// If a rendition change is needed, then it would've be done on `bandwidthupdate`.
|
||||
// Here the only consideration is that for buffer based ABR there's no guarantee
|
||||
// of an immediate switch (since the bandwidth is averaged with a timeout
|
||||
// bandwidth value of 1), so force a load on the segment loader to keep it going.
|
||||
_this4.mainSegmentLoader_.load();
|
||||
}
|
||||
}); // `progress` events are not reliable enough of a bandwidth measure to trigger buffer
|
||||
// based ABR.
|
||||
|
||||
if (!this.experimentalBufferBasedABR) {
|
||||
this.mainSegmentLoader_.on('bandwidthupdate', function () {
|
||||
var nextPlaylist = _this4.selectPlaylist();
|
||||
|
||||
if (_this4.shouldSwitchToMedia_(nextPlaylist)) {
|
||||
_this4.switchMedia_(nextPlaylist, 'bandwidthupdate');
|
||||
}
|
||||
|
||||
_this4.tech_.trigger('bandwidthupdate');
|
||||
});
|
||||
this.mainSegmentLoader_.on('progress', function () {
|
||||
_this4.trigger('progress');
|
||||
});
|
||||
|
@ -30257,6 +30438,7 @@
|
|||
this.width = resolution && resolution.width;
|
||||
this.height = resolution && resolution.height;
|
||||
this.bandwidth = playlist.attributes.BANDWIDTH;
|
||||
this.frameRate = playlist.attributes['FRAME-RATE'];
|
||||
}
|
||||
|
||||
this.codecs = codecsForPlaylist(mpc.master(), playlist);
|
||||
|
@ -31096,13 +31278,13 @@
|
|||
initPlugin(this, options);
|
||||
};
|
||||
|
||||
var version$4 = "2.14.3";
|
||||
var version$4 = "2.16.0";
|
||||
|
||||
var version$3 = "6.0.1";
|
||||
|
||||
var version$2 = "0.21.1";
|
||||
var version$2 = "0.22.1";
|
||||
|
||||
var version$1 = "4.7.1";
|
||||
var version$1 = "4.8.0";
|
||||
|
||||
var version = "3.1.3";
|
||||
|
||||
|
@ -32364,23 +32546,33 @@
|
|||
return tech.vhs;
|
||||
},
|
||||
canPlayType: function canPlayType(type, options) {
|
||||
var simpleType = simpleTypeFromSourceType(type);
|
||||
|
||||
if (!simpleType) {
|
||||
return '';
|
||||
}
|
||||
|
||||
var overrideNative = VhsSourceHandler.getOverrideNative(options);
|
||||
var supportsTypeNatively = Vhs.supportsTypeNatively(simpleType);
|
||||
var canUseMsePlayback = !supportsTypeNatively || overrideNative;
|
||||
return canUseMsePlayback ? 'maybe' : '';
|
||||
},
|
||||
getOverrideNative: function getOverrideNative(options) {
|
||||
if (options === void 0) {
|
||||
options = {};
|
||||
}
|
||||
|
||||
var _videojs$mergeOptions = videojs__default["default"].mergeOptions(videojs__default["default"].options, options),
|
||||
_videojs$mergeOptions2 = _videojs$mergeOptions.vhs;
|
||||
|
||||
_videojs$mergeOptions2 = _videojs$mergeOptions2 === void 0 ? {} : _videojs$mergeOptions2;
|
||||
var _videojs$mergeOptions3 = _videojs$mergeOptions2.overrideNative,
|
||||
overrideNative = _videojs$mergeOptions3 === void 0 ? !videojs__default["default"].browser.IS_ANY_SAFARI : _videojs$mergeOptions3,
|
||||
_videojs$mergeOptions4 = _videojs$mergeOptions.hls;
|
||||
_videojs$mergeOptions4 = _videojs$mergeOptions4 === void 0 ? {} : _videojs$mergeOptions4;
|
||||
var _videojs$mergeOptions5 = _videojs$mergeOptions4.overrideNative,
|
||||
legacyOverrideNative = _videojs$mergeOptions5 === void 0 ? false : _videojs$mergeOptions5;
|
||||
var supportedType = simpleTypeFromSourceType(type);
|
||||
var canUseMsePlayback = supportedType && (!Vhs.supportsTypeNatively(supportedType) || legacyOverrideNative || overrideNative);
|
||||
return canUseMsePlayback ? 'maybe' : '';
|
||||
var _options = options,
|
||||
_options$vhs = _options.vhs,
|
||||
vhs = _options$vhs === void 0 ? {} : _options$vhs,
|
||||
_options$hls = _options.hls,
|
||||
hls = _options$hls === void 0 ? {} : _options$hls;
|
||||
var defaultOverrideNative = !(videojs__default["default"].browser.IS_ANY_SAFARI || videojs__default["default"].browser.IS_IOS);
|
||||
var _vhs$overrideNative = vhs.overrideNative,
|
||||
overrideNative = _vhs$overrideNative === void 0 ? defaultOverrideNative : _vhs$overrideNative;
|
||||
var _hls$overrideNative = hls.overrideNative,
|
||||
legacyOverrideNative = _hls$overrideNative === void 0 ? false : _hls$overrideNative;
|
||||
return legacyOverrideNative || overrideNative;
|
||||
}
|
||||
};
|
||||
/**
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue