tiny-essentials
Version:
Collection of small, essential scripts designed to be used across various projects. These simple utilities are crafted for speed, ease of use, and versatility.
1 lines • 20.8 kB
JavaScript
(()=>{"use strict";var t={7(t){var e,r="object"==typeof Reflect?Reflect:null,n=r&&"function"==typeof r.apply?r.apply:function(t,e,r){return Function.prototype.apply.call(t,e,r)};e=r&&"function"==typeof r.ownKeys?r.ownKeys:Object.getOwnPropertySymbols?function(t){return Object.getOwnPropertyNames(t).concat(Object.getOwnPropertySymbols(t))}:function(t){return Object.getOwnPropertyNames(t)};var i=Number.isNaN||function(t){return t!=t};function o(){o.init.call(this)}t.exports=o,t.exports.once=function(t,e){return new Promise(function(r,n){function i(r){t.removeListener(e,o),n(r)}function o(){"function"==typeof t.removeListener&&t.removeListener("error",i),r([].slice.call(arguments))}f(t,e,o,{once:!0}),"error"!==e&&function(t,e){"function"==typeof t.on&&f(t,"error",e,{once:!0})}(t,i)})},o.EventEmitter=o,o.prototype._events=void 0,o.prototype._eventsCount=0,o.prototype._maxListeners=void 0;var s=10;function a(t){if("function"!=typeof t)throw new TypeError('The "listener" argument must be of type Function. Received type '+typeof t)}function l(t){return void 0===t._maxListeners?o.defaultMaxListeners:t._maxListeners}function u(t,e,r,n){var i,o,s,u;if(a(r),void 0===(o=t._events)?(o=t._events=Object.create(null),t._eventsCount=0):(void 0!==o.newListener&&(t.emit("newListener",e,r.listener?r.listener:r),o=t._events),s=o[e]),void 0===s)s=o[e]=r,++t._eventsCount;else if("function"==typeof s?s=o[e]=n?[r,s]:[s,r]:n?s.unshift(r):s.push(r),(i=l(t))>0&&s.length>i&&!s.warned){s.warned=!0;var c=new Error("Possible EventEmitter memory leak detected. "+s.length+" "+String(e)+" listeners added. Use emitter.setMaxListeners() to increase limit");c.name="MaxListenersExceededWarning",c.emitter=t,c.type=e,c.count=s.length,u=c,console&&console.warn&&console.warn(u)}return t}function c(){if(!this.fired)return this.target.removeListener(this.type,this.wrapFn),this.fired=!0,0===arguments.length?this.listener.call(this.target):this.listener.apply(this.target,arguments)}function h(t,e,r){var n={fired:!1,wrapFn:void 0,target:t,type:e,listener:r},i=c.bind(n);return i.listener=r,n.wrapFn=i,i}function p(t,e,r){var n=t._events;if(void 0===n)return[];var i=n[e];return void 0===i?[]:"function"==typeof i?r?[i.listener||i]:[i]:r?function(t){for(var e=new Array(t.length),r=0;r<e.length;++r)e[r]=t[r].listener||t[r];return e}(i):y(i,i.length)}function d(t){var e=this._events;if(void 0!==e){var r=e[t];if("function"==typeof r)return 1;if(void 0!==r)return r.length}return 0}function y(t,e){for(var r=new Array(e),n=0;n<e;++n)r[n]=t[n];return r}function f(t,e,r,n){if("function"==typeof t.on)n.once?t.once(e,r):t.on(e,r);else{if("function"!=typeof t.addEventListener)throw new TypeError('The "emitter" argument must be of type EventEmitter. Received type '+typeof t);t.addEventListener(e,function i(o){n.once&&t.removeEventListener(e,i),r(o)})}}Object.defineProperty(o,"defaultMaxListeners",{enumerable:!0,get:function(){return s},set:function(t){if("number"!=typeof t||t<0||i(t))throw new RangeError('The value of "defaultMaxListeners" is out of range. It must be a non-negative number. Received '+t+".");s=t}}),o.init=function(){void 0!==this._events&&this._events!==Object.getPrototypeOf(this)._events||(this._events=Object.create(null),this._eventsCount=0),this._maxListeners=this._maxListeners||void 0},o.prototype.setMaxListeners=function(t){if("number"!=typeof t||t<0||i(t))throw new RangeError('The value of "n" is out of range. It must be a non-negative number. Received '+t+".");return this._maxListeners=t,this},o.prototype.getMaxListeners=function(){return l(this)},o.prototype.emit=function(t){for(var e=[],r=1;r<arguments.length;r++)e.push(arguments[r]);var i="error"===t,o=this._events;if(void 0!==o)i=i&&void 0===o.error;else if(!i)return!1;if(i){var s;if(e.length>0&&(s=e[0]),s instanceof Error)throw s;var a=new Error("Unhandled error."+(s?" ("+s.message+")":""));throw a.context=s,a}var l=o[t];if(void 0===l)return!1;if("function"==typeof l)n(l,this,e);else{var u=l.length,c=y(l,u);for(r=0;r<u;++r)n(c[r],this,e)}return!0},o.prototype.addListener=function(t,e){return u(this,t,e,!1)},o.prototype.on=o.prototype.addListener,o.prototype.prependListener=function(t,e){return u(this,t,e,!0)},o.prototype.once=function(t,e){return a(e),this.on(t,h(this,t,e)),this},o.prototype.prependOnceListener=function(t,e){return a(e),this.prependListener(t,h(this,t,e)),this},o.prototype.removeListener=function(t,e){var r,n,i,o,s;if(a(e),void 0===(n=this._events))return this;if(void 0===(r=n[t]))return this;if(r===e||r.listener===e)0===--this._eventsCount?this._events=Object.create(null):(delete n[t],n.removeListener&&this.emit("removeListener",t,r.listener||e));else if("function"!=typeof r){for(i=-1,o=r.length-1;o>=0;o--)if(r[o]===e||r[o].listener===e){s=r[o].listener,i=o;break}if(i<0)return this;0===i?r.shift():function(t,e){for(;e+1<t.length;e++)t[e]=t[e+1];t.pop()}(r,i),1===r.length&&(n[t]=r[0]),void 0!==n.removeListener&&this.emit("removeListener",t,s||e)}return this},o.prototype.off=o.prototype.removeListener,o.prototype.removeAllListeners=function(t){var e,r,n;if(void 0===(r=this._events))return this;if(void 0===r.removeListener)return 0===arguments.length?(this._events=Object.create(null),this._eventsCount=0):void 0!==r[t]&&(0===--this._eventsCount?this._events=Object.create(null):delete r[t]),this;if(0===arguments.length){var i,o=Object.keys(r);for(n=0;n<o.length;++n)"removeListener"!==(i=o[n])&&this.removeAllListeners(i);return this.removeAllListeners("removeListener"),this._events=Object.create(null),this._eventsCount=0,this}if("function"==typeof(e=r[t]))this.removeListener(t,e);else if(void 0!==e)for(n=e.length-1;n>=0;n--)this.removeListener(t,e[n]);return this},o.prototype.listeners=function(t){return p(this,t,!0)},o.prototype.rawListeners=function(t){return p(this,t,!1)},o.listenerCount=function(t,e){return"function"==typeof t.listenerCount?t.listenerCount(e):d.call(t,e)},o.prototype.listenerCount=d,o.prototype.eventNames=function(){return this._eventsCount>0?e(this._events):[]}}};const e={};function r(n){const i=e[n];if(void 0!==i)return i.exports;const o=e[n]={exports:{}};return t[n](o,o.exports,r),o.exports}r.d=(t,e)=>{if(Array.isArray(e))for(var n=0;n<e.length;){var i=e[n++],o=e[n++];r.o(t,i)?0===o&&n++:0===o?Object.defineProperty(t,i,{enumerable:!0,value:e[n++]}):Object.defineProperty(t,i,{enumerable:!0,get:o})}else for(var i in e)r.o(e,i)&&!r.o(t,i)&&Object.defineProperty(t,i,{enumerable:!0,get:e[i]})},r.o=(t,e)=>Object.prototype.hasOwnProperty.call(t,e);let n={};r.d(n,{TinyMediaPlayer:()=>d});var i=r(7);const o=new Map;class s extends Error{constructor(t,e,r){super(t),this.name="MediaLoadingError",this.url=e,this.stage=r}}const a=async t=>{const e=(new TextEncoder).encode(t),r=await crypto.subtle.digest("SHA-1",e);return Array.from(new Uint8Array(r)).map(t=>t.toString(16).padStart(2,"0")).join("").substring(0,8)},l=(t,e="image/jpeg")=>{const r=t=>{const e=URL.createObjectURL(t),r=o.get(e);return o.set(e,"number"==typeof r?r+1:1),e};if(t instanceof Uint8Array)return r(new Blob([t],{type:e}));if("string"==typeof t&&t.startsWith("data:")){const n=t.split(",")[1],i=atob(n),o=new Uint8Array(i.length);for(let t=0;t<i.length;t++)o[t]=i.charCodeAt(t);return r(new Blob([o],{type:e}))}return"string"==typeof t?t:""},u=(t,e)=>{const r=t=>e&&void 0===t,n=t=>r(t)||"string"==typeof t||null===t,i=(t,e)=>!!r(t)||!(!Array.isArray(t)||!t.every(e));if(!n(t.title))throw new TypeError('Invalid metadata: "title" must be a string or null.');if(!n(t.album))throw new TypeError('Invalid metadata: "album" must be a string or null.');if(!n(t.albumartist))throw new TypeError('Invalid metadata: "albumartist" must be a string or null.');if(!n(t.artist))throw new TypeError('Invalid metadata: "artist" must be a string or null.');if(o=t.year,!r(o)&&"number"!=typeof o&&null!==o)throw new TypeError('Invalid metadata: "year" must be a number or null.');var o;if(!i(t.albumartists,t=>"string"==typeof t))throw new TypeError('Invalid metadata: "albumartists" must be an array of string.');if(!i(t.genre,t=>"string"==typeof t))throw new TypeError('Invalid metadata: "genre" must be an array of string.');if(!i(t.label,t=>"string"==typeof t))throw new TypeError('Invalid metadata: "label" must be an array of string.');if(!i(t.composer,t=>"string"==typeof t))throw new TypeError('Invalid metadata: "composer" must be an array of string.');if(!i(t.artists,t=>"string"==typeof t))throw new TypeError('Invalid metadata: "artists" must be an array of string.');if(!i(t.picture,t=>!(void 0!==t.description&&"string"!=typeof t.description||void 0!==t.name&&"string"!=typeof t.name||void 0!==t.type&&"string"!=typeof t.type||"string"!=typeof t.format||!(t.data instanceof Uint8Array||"string"==typeof t.data))))throw new TypeError('Invalid metadata: "picture" must be an array of pictures.');const s=(t,e)=>{if(!r(t)){if(null==t)throw new TypeError(`Invalid metadata: "${e}" is required.`);if("object"!=typeof t)throw new TypeError(`Invalid metadata: "${e}" must be an object or null.`);if("number"!=typeof t.no&&null!==t.no)throw new TypeError(`Invalid metadata: "${e}.no" must be a number or null.`);if("number"!=typeof t.of&&null!==t.of)throw new TypeError(`Invalid metadata: "${e}.of" must be a number or null.`)}};s(t.disk,"disk"),s(t.track,"track")},c=t=>u(t,!1);class h{constructor(){if(new.target===h)throw new Error("BaseMediaAdapter is an abstract class and cannot be instantiated directly.")}canHandle(t){throw new Error('Method "canHandle" must be implemented by the subclass.')}async play(t){throw new Error('Method "play" must be implemented by the subclass.')}async pause(){throw new Error('Method "pause" must be implemented by the subclass.')}async stop(){throw new Error('Method "stop" must be implemented by the subclass.')}async seek(t){throw new Error('Method "seek" must be implemented by the subclass.')}getCurrentTime(){throw new Error('Method "getCurrentTime" must be implemented by the subclass.')}setVolume(t){throw new Error('Method "setVolume" must be implemented by the subclass.')}}class p extends i.EventEmitter{static BaseMediaAdapter=h;static#t="Unknown Artist";static get unknownArtist(){return p.#t}static set unknownArtist(t){if("string"!=typeof t&&"function"!=typeof t)throw new TypeError("unknownArtist must have an string or function.");p.#t=t}static async parseContent(t,e,r,n,i){return(async(t,e={},r={},n=t=>new Promise((t,e)=>e(new TypeError("parseFile library not found."))),i={},o="null")=>{if("string"!=typeof t&&!(t instanceof HTMLMediaElement))throw new TypeError("Source must be a string or an HTMLMediaElement.");if(i.onProgress&&"function"!=typeof i.onProgress)throw new TypeError("callbacks.onProgress must be a function.");if(i.onError&&"function"!=typeof i.onError)throw new TypeError("callbacks.onError must be a function.");let c,h="";const p=(e,r)=>{i.onProgress&&i.onProgress({event:r,status:"loading",stage:e,url:h||(t instanceof HTMLMediaElement?t.src:"unknown")})},d=(e,r)=>{i.onError&&i.onError({error:e,url:h||(t instanceof HTMLMediaElement?t.src:"unknown"),stage:r})};try{p("INITIALIZING"),"string"==typeof t?(h=t,c=new Audio(h)):(c=t,h=c.src);try{await new Promise((t,e)=>{const r=t=>p("DOWNLOADING",t),n=()=>{o(),t(void 0)},i=t=>{o(),e(new Error(`HTMLMediaElement failed to load: ${t.message||"Unknown error"}`))},o=()=>{c.removeEventListener("progress",r),c.removeEventListener("loadedmetadata",n),c.removeEventListener("error",i)};c.addEventListener("progress",r),c.addEventListener("loadedmetadata",n),c.addEventListener("error",i),c.readyState>=1&&(o(),t(void 0))}),p("METADATA_LOADED")}catch(t){throw new s(t instanceof Error?t.message:"UNKNOWN ERROR",h,"METADATA")}const y={id:r.id||await a(h),duration:Math.floor(1e3*c.duration),url:h,weight:r.weight??1};let f={};p("EXTRACTING_ID3");try{f=await(async(t,e)=>{if("string"!=typeof t)throw new TypeError(`Expected url to be a string, but received ${typeof t}.`);if("function"!=typeof e)throw new TypeError(`Expected parseFile to be a function, but received ${typeof e}.`);try{const r=await fetch(t);if(!r.ok)throw new Error(`Failed to fetch audio file: ${r.status} ${r.statusText}`);const n=await r.blob(),i=await e(n);if(!i||"object"!=typeof i.common)throw new TypeError('Invalid metadata: "common" property is missing or not an object.');const o=i.common;return(t=>{u(t,!0)})(o),{title:o?.title??null,album:o?.album??null,albumartist:o?.albumartist??null,albumartists:o?.albumartists??[],genre:o?.genre??[],label:o?.label??[],composer:o?.composer??[],year:o?.year??null,artist:o?.artist??null,artists:o?.artists??[],disk:o?.disk?{no:o.disk.no,of:o.disk.of}:{no:null,of:null},track:o?.track?{no:o.track.no,of:o.track.of}:{no:null,of:null},picture:o?.picture?.map(t=>({...t,data:l(t.data,t.format)}))??[]}}catch(t){throw t}})(h,n)}catch(t){d(t instanceof Error?t:new Error("Unknown Error"),"ID3"),console.warn(`[TinyAudioUtils] ID3 extraction failed for ${h}. Falling back to filename.`)}const m=t=>{try{return(t.split(/[?#]/)[0].split("/").pop()??"").replace(/\.[^/.]+$/,"")||"Unknown Track"}catch{return"Unknown Track"}},g={...y,...e,...f,...r,title:f.title||r.title||m(h),artist:f.artist||r.artist||("string"==typeof o?o:String(o()))};return i.onProgress&&i.onProgress({status:"success",stage:"COMPLETE",url:h}),g}catch(t){if(t instanceof s)throw t;{const e=new s(t instanceof Error?t.message:"UNKNOWN ERROR",h,"INITIALIZING");throw d(e,"INITIALIZING"),e}}})(t,e,r,n,i,p.#t)}#e=new Map;#r=[];#n=-1;#i="NONE";#o=!1;#s=!1;#a=1;#l;#u;constructor(t={}){super(),this.#l=Boolean(t.persistVolume),this.#u="string"==typeof t.volumeStorageKey?t.volumeStorageKey:"tiny_media_player_volume",this.#l&&this.#c()}#c(){try{if("undefined"!=typeof window&&window.localStorage){const t=window.localStorage.getItem(this.#u);if(null!==t){const e=parseFloat(t);!isNaN(e)&&e>=0&&e<=1&&(this.#a=e)}}}catch(t){console.warn("[TinyMediaPlayer] Failed to read volume from localStorage.",t)}}#h(){try{this.#l&&"undefined"!=typeof window&&window.localStorage&&window.localStorage.setItem(this.#u,this.#a.toString())}catch(t){console.warn("[TinyMediaPlayer] Failed to save volume to localStorage.",t)}}_getWeightedRandomIndex(){if(0===this.#r.length)return-1;if(1===this.#r.length)return 0;let t=0;for(let e=0;e<this.#r.length;e++){if(e===this.#n)continue;const r=this.#r[e];t+="number"==typeof r.weight&&r.weight>0?r.weight:1}let e=Math.random()*t;for(let t=0;t<this.#r.length;t++){if(t===this.#n)continue;const r=this.#r[t],n="number"==typeof r.weight&&r.weight>0?r.weight:1;if(e<n)return t;e-=n}return this.#n===this.#r.length-1?0:this.#r.length-1}get playlist(){return[...this.#r]}set playlist(t){if(!Array.isArray(t))throw new TypeError("Playlist must be an array of MediaContent objects.");t.forEach(t=>c(t));const e=this.#s;let r=null;if(-1!==this.#n&&this.#r.length>0)try{r=this.#p()}catch(t){}this.#r=t,this.emit("playlistUpdate",this.playlist),0===this.#r.length?this.#n=-1:(this.#n>=this.#r.length||-1===this.#n)&&(this.#n=0),this.emit("trackChange",this.#n),e&&(async()=>{try{r&&await r.stop(),-1!==this.#n&&await this.play()}catch(t){console.warn("[TinyMediaPlayer] Background transition error on playlist update:",t)}})()}get currentIndex(){return this.#n}set currentIndex(t){if("number"!=typeof t)throw new TypeError("Current index must be a number.");if(t<-1||t>=this.#r.length&&this.#r.length>0)throw new RangeError(`Index ${t} is out of bounds for the current playlist.`);if(this.#n===t)return;const e=this.#s;let r=null;if(-1!==this.#n&&this.#r.length>0)try{r=this.#p()}catch(t){}this.#n=t,this.emit("trackChange",this.#n),e&&(async()=>{try{r&&await r.stop(),-1!==this.#n&&await this.play()}catch(t){console.warn("[TinyMediaPlayer] Background transition error on index update:",t)}})()}get loopMode(){return this.#i}set loopMode(t){const e=["NONE","TRACK","PLAYLIST"];if(!e.includes(t))throw new TypeError(`Loop mode must be one of: ${e.join(", ")}.`);this.#i=t,this.emit("loopModeChange",this.#i)}get isRandom(){return this.#o}set isRandom(t){if("boolean"!=typeof t)throw new TypeError("Random mode state must be a boolean.");this.#o=t,this.emit("randomModeChange",this.#o)}get isPlaying(){return this.#s}get volume(){return this.#a}set volume(t){if("number"!=typeof t)throw new TypeError("Volume must be a number.");if(t<0||t>1)throw new RangeError("Volume must be tightly constrained between 0.0 and 1.0.");if(this.#a=t,this.#h(),this.emit("volumeChange",this.#a),-1!==this.#n&&this.#r.length>0)try{const t=this.#p();t&&t.setVolume(this.#a)}catch(t){}}get persistVolume(){return this.#l}set persistVolume(t){if("boolean"!=typeof t)throw new TypeError("Persist volume parameter must be a boolean.");this.#l=t,t&&this.#h()}get volumeStorageKey(){return this.#u}set volumeStorageKey(t){if("string"!=typeof t)throw new TypeError("Volume storage key must be a string.");if(""===t.trim())throw new TypeError("Volume storage key cannot be an empty string.");const e=this.#u;this.#u=t;try{this.#l&&"undefined"!=typeof window&&window.localStorage&&(window.localStorage.removeItem(e),this.#h())}catch(t){console.warn("[TinyMediaPlayer] Failed to migrate volume storage key.",t)}}registerAdapter(t,e){if("string"!=typeof t)throw new TypeError("Adapter ID must be a string.");if(!(e instanceof h))throw new TypeError("Adapter must be an instance of BaseMediaAdapter.");this.#e.set(t,e)}#p(){if(-1===this.#n||0===this.#r.length)return null;const t=this.#r[this.#n];for(const e of this.#e.values())if(e.canHandle(t))return e;throw new Error(`No compatible adapter found for content ID: ${t.id}.`)}addTrack(t){if(!t||"object"!=typeof t||"string"!=typeof t.url)throw new TypeError("Track content must be a valid MediaContent object containing a URL.");const e={id:"",title:"",artist:"",url:"",duration:0,weight:1,title:null,album:null,albumartist:null,albumartists:[],genre:[],label:[],composer:[],year:null,artist:null,artists:[],disk:{no:null,of:null},track:{no:null,of:null},picture:[],...t};c(e);const r=this.#r.push(e);return-1===this.#n&&(this.#n=0,this.emit("trackChange",this.#n)),this.emit("playlistUpdate",this.playlist),r}existsTrack(t){if("number"!=typeof t)throw new TypeError("Index must be a number.");return t>=0&&t<this.#r.length}getTrack(t){if("number"!=typeof t)throw new TypeError("Index must be a number.");if(!this.existsTrack(t))throw new RangeError(`Index ${t} is out of bounds for the current playlist.`);return this.#r[t]}async removeTrack(t){if("number"!=typeof t)throw new TypeError("Index must be a number.");if(!this.existsTrack(t))throw new RangeError(`Index ${t} is out of bounds for the current playlist.`);t===this.#n?(await this.stop(),this.#r.splice(t,1),0===this.#r.length?this.#n=-1:this.#n>=this.#r.length&&(this.#n=0),this.emit("trackChange",this.#n)):(this.#r.splice(t,1),t<this.#n&&(this.#n-=1,this.emit("trackChange",this.#n))),this.emit("playlistUpdate",this.playlist)}searchTrack(t){if("string"!=typeof t&&"function"!=typeof t)throw new TypeError("Search query must be a string or a boolean evaluation function.");const e=[];for(let r=0;r<this.#r.length;r++){const n=this.#r[r];let i=!1;if("function"==typeof t)i=Boolean(t(n));else{const e=t.toLowerCase(),r=n.title&&n.title.toLowerCase().includes(e),o=n.artist&&n.artist.toLowerCase().includes(e),s=n.album&&n.album.toLowerCase().includes(e);i=Boolean(r||o||s)}i&&e.push({track:n,index:r})}return e}async clearPlaylist(){await this.stop(),this.#r=[],this.#n=-1,this.emit("playlistUpdate",this.playlist),this.emit("trackChange",this.#n)}async play(){const t=this.#p();t&&(t.setVolume(this.#a),await t.play(this.#r[this.#n]),this.#s=!0,this.emit("play",this.#n))}async pause(){const t=this.#p();t&&(await t.pause(),this.#s=!1,this.emit("pause",this.#n))}async stop(){const t=this.#p();t&&(await t.stop(),this.#s=!1,this.emit("stop",this.#n))}async next(){if(0!==this.#r.length)if(await this.stop(),"TRACK"!==this.#i){if(this.#o)this.#n=this._getWeightedRandomIndex();else{let t=this.#n+1;if(t>=this.#r.length){if("PLAYLIST"!==this.#i)return;t=0}this.#n=t}this.emit("trackChange",this.#n),await this.play()}else await this.play()}async prev(){if(0!==this.#r.length){if(await this.stop(),this.#o)this.#n=this._getWeightedRandomIndex();else{let t=this.#n-1;if(t<0){if("PLAYLIST"!==this.#i)return;t=this.#r.length-1}this.#n=t}this.emit("trackChange",this.#n),await this.play()}}async seek(t){if("number"!=typeof t)throw new TypeError("Seek time must be a number in milliseconds.");if(t<0)throw new RangeError("Seek time cannot be negative.");const e=this.#p();e&&(await e.seek(t),this.emit("seek",t))}async step(t){if("number"!=typeof t)throw new TypeError("Step amount must be a number in milliseconds.");const e=this.#p();if(!e)return;let r=e.getCurrentTime()+t;r<0&&(r=0),await this.seek(r)}}const d=p;window.TinyMediaPlayer=n.TinyMediaPlayer})();