Closed michaelschufi closed 3 years ago
I'm pretty sure this is happening because icecast-metadata-player doesn't have a main
entry point in the package.json. Since Next.js does server side rendering, it's looking for a way to bundle the main NodeJS entry point, and it appears to be ignoring the browser entry point. I went ahead and added main
in the package.json in this branch, if you want to pull that down and give that a try: https://github.com/eshaz/icecast-metadata-js/tree/fix-exports
Also, you will need to instantiate the player client side. icecast-metadata-player creates the audio element and accesses a few browser APIs when a new instance is created. I think you could accomplish this by wrapping the instantiation in a useEffect
hook which is what is suggested here: https://github.com/vercel/next.js/discussions/17963#discussioncomment-102745
Let me know if that helps.
Hi
Thanks for your quick reply!
I've tried it, using this process:
fix-exports
.icecast-metadata-player
and icecast-metadata-js
packages using npm link
in the specific folders in /src
as global npm packages on my machine.npm link icecast-metadata-player
and npm link icecast-metadata-js
.import IcecastMetadataPlayer from 'icecast-metadata-player';
new IcecastMetadataPlayer(...)
only in client-side code.Unfortunately, now Next.js throws errors during the compilation (dev mode):
Error: Debug Failure. False expression.
at getConstructorDefinedThisAssignmentTypes (/path/to/my/project/node_modules/typescript/lib/typescript.js:50877:22)
at getWidenedTypeForAssignmentDeclaration (/path/to/my/project/node_modules/typescript/lib/typescript.js:50713:67)
at getTypeOfVariableOrParameterOrPropertyWorker (/path/to/my/project/node_modules/typescript/lib/typescript.js:51077:24)
at getTypeOfVariableOrParameterOrProperty (/path/to/my/project/node_modules/typescript/lib/typescript.js:51020:28)
at getTypeOfSymbol (/path/to/my/project/node_modules/typescript/lib/typescript.js:51353:24)
at getTypeOfInstantiatedSymbol (/path/to/my/project/node_modules/typescript/lib/typescript.js:51305:44)
at getTypeOfSymbol (/path/to/my/project/node_modules/typescript/lib/typescript.js:51344:24)
at /path/to/my/project/node_modules/typescript/lib/typescript.js:75447:36
at Object.forEach (/path/to/my/project/node_modules/typescript/lib/typescript.js:382:30)
at checkIndexConstraints (/path/to/my/project/node_modules/typescript/lib/typescript.js:75446:20)
at checkClassLikeDeclaration (/path/to/my/project/node_modules/typescript/lib/typescript.js:75769:17)
at checkClassDeclaration (/path/to/my/project/node_modules/typescript/lib/typescript.js:75664:13)
at checkSourceElementWorker (/path/to/my/project/node_modules/typescript/lib/typescript.js:77034:28)
at checkSourceElement (/path/to/my/project/node_modules/typescript/lib/typescript.js:76875:17)
at Object.forEach (/path/to/my/project/node_modules/typescript/lib/typescript.js:382:30)
at checkBlock (/path/to/my/project/node_modules/typescript/lib/typescript.js:74031:20)
Adding type declarations with typescript makes no difference. I've tried it using this:
// NOTE: This is incomplete! I just typed the part of the api that I need.
declare module 'icecast-metadata-player' {
interface MetadataIcy {
StreamTitle: string;
}
interface IcecastMetadataPlayerOptions {
audioElement?: HTMLAudioElement;
enableLogging?: boolean;
retryTimeout?: number;
metadataTypes?: ('icy' | 'ogg')[];
// Callbacks
// Called when metadata is synchronized with the audio.
onMetadata?: (
metadata: MetadataIcy,
timestampOffset: number,
timestamp: number,
) => void;
// Called when the fetch request is started.
onLoad?: () => void;
// Called when fetch request begins to return data.
onStreamStart?: () => void;
// Called when the audio element begins playing.
onPlay?: () => void;
// Called when stream data is sent to the audio element.
onStream?: (streamData: never) => void;
// Called when the fetch request completes.
onStreamEnd?: () => void;
// Called when the stream is completely stopped and all cleanup operations are complete.
onStop?: () => void;
}
class IcecastMetadataPlayer {
static canPlayType(mimeType: string): '' | 'maybe' | 'probably';
constructor(endpoint: string, options?: IcecastMetadataPlayerOptions);
play(): Promise<void>;
stop(): Promise<void>;
detachAudioElement(): void;
audioElement: HTMLAudioElement;
icyMetaInt: number;
state: 'loading' | 'playing' | 'stopping' | 'stopped' | 'retrying';
}
export default IcecastMetadataPlayer;
}
The typescript declarations are a good idea, and having those in this project would be helpful.
Could you try updating your package.json
file to reference the path to the git clone on your computer? I find it easier to import local dependencies this way vs. using npm link
. You should only need to have icecast-metadata-player
as a dependency and not have to do anything with icecast-metadata-js
. Those are actually different NPM packages, even through both of them exist in this repository.
I was able to get the example code you have to compile by following these steps:
"icecast-metadata-player": "1.2.2",
to "icecast-metadata-player": "file:../icecast-metadata-js/src/icecast-metadata-player"
in package.json
where the file path is the relative path from your project to the git clone you have :
"dependencies": {
"icecast-metadata-player": "file:../icecast-metadata-js/src/icecast-metadata-player",
"next": "latest",
"react": "^16.13.1",
"react-dom": "^16.13.1"
},
npm i
npm start
Hi @eshaz
Unfortunately, importing it directly, didn't workout. I assume you didn't test it with a typescript Next.js project.
I've created a reproduction repository to showcase the error: https://github.com/michaelschufi/icecast-metadata-player-test
This is basically Next.js' typescript custom server example.
As you will see it throws the same error as I have posted.
I wasn't able to reproduce the error using your test repo. Everything compiled and ran nicely for me. Nothing played, but there wasn't any code in there that would have started playback. Maybe you need to delete your node_modules
folder or something like that? I don't believe this compilation error is an issue with IcecastMetadataPlayer based on the information we have so far.
I really like the idea of typescript definitions in this project, so I went ahead and expanded upon what you typed up and added those to the fix-exports
branch.
As I was testing out the new type definitions, I also updated your example by using the React hooks to tell Next.js to instantiate IcecastMetadataPlayer client side so you can play back the stream successfully. Feel free to use this in your code base:
src/pages/index.tsx
:
import IcecastMetadataPlayer, {IcyMetadata} from "icecast-metadata-player";
import { NextPage } from "next";
import { useState, useEffect } from "react";
const MyComponent = () => {
const [player, setPlayer] = useState<IcecastMetadataPlayer>() as [IcecastMetadataPlayer, any];
const [metadata, setMetadata] = useState<string>();
useEffect(() => {
setPlayer(new IcecastMetadataPlayer("https://dsmrad.io/stream/isics-all", {
onMetadata: (metadata: IcyMetadata) => {setMetadata(metadata.StreamTitle)}
}))
}, [])
return (
<>
<button onClick={() => player.play()}>Play</button>
<button onClick={() => player.stop()}>Stop</button>
<div>{metadata}</div>
</>
)
};
const Home: NextPage<Record<string, never>> = () => <MyComponent />;
export default Home;
I'm going to put a PR in to track these updates in the fix-exports
branch and I plan on merging and released it within the next few days. I'll leave the PR open for a few days to give you a chance to put in some feedback / comments if you want.
I wasn't able to reproduce the error using your test repo. Everything compiled and ran nicely for me. Nothing played, but there wasn't any code in there that would have started playback. Maybe you need to delete your
node_modules
folder or something like that? I don't believe this compilation error is an issue with IcecastMetadataPlayer based on the information we have so far.
That's really strange. For me the error happens only after I open the page in the browser (error is in the server console). Or when hot reloading (though not in this bare-bones project).
I really like the idea of typescript definitions in this project, so I went ahead and expanded upon what you typed up and added those to the
fix-exports
branch.
That sounds awesome!
As I was testing out the new type definitions, I also updated your example by using the React hooks to tell Next.js to instantiate IcecastMetadataPlayer client side so you can play back the stream successfully. Feel free to use this in your code base:
src/pages/index.tsx
:import IcecastMetadataPlayer, {IcyMetadata} from "icecast-metadata-player"; import { NextPage } from "next"; import { useState, useEffect } from "react"; const MyComponent = () => { const [player, setPlayer] = useState<IcecastMetadataPlayer>() as [IcecastMetadataPlayer, any]; const [metadata, setMetadata] = useState<string>(); useEffect(() => { setPlayer(new IcecastMetadataPlayer("https://dsmrad.io/stream/isics-all", { onMetadata: (metadata: IcyMetadata) => {setMetadata(metadata.StreamTitle)} })) }, []) return ( <> <button onClick={() => player.play()}>Play</button> <button onClick={() => player.stop()}>Stop</button> <div>{metadata}</div> </> ) }; const Home: NextPage<Record<string, never>> = () => <MyComponent />; export default Home;
I'm doing this in the bigger project already. Doesn't seem to be the source of the problem. And it didn't fix it when I updated this code.
I'm going to put a PR in to track these updates in the
fix-exports
branch and I plan on merging and released it within the next few days. I'll leave the PR open for a few days to give you a chance to put in some feedback / comments if you want.
Cool! Looks good at a first glance.
I'm really wondering why you cannot reproduce the error.
Could you maybe release the fix-export
branch with a specific tag to npm? This way, I can try reproducing the error on an online sandbox service.
@michaelschufi I released icecast-metadata-player/1.3.1 with all of the changes in fix-exports
. Merging the PR automatically closed this issue, but if you are still having problems with it please go ahead and reopen this issue.
It's possible that we are using different versions of Node / NPM, or maybe the OS is impacting this in some way? Maybe you have some customization on your environment that I don't?
I'm using: nvm: 7.7.6 Node: 15.14.0 OS: Linux 5.8.0-53-generic 20.04.1-Ubuntu SMP Thu May 6 09:52:46 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux
@eshaz Sounds good! Unfortunately, the import doesn't work. I've updated the reproduction repo and it's reproducible on CodeSandbox: https://codesandbox.io/s/suspicious-yonath-xnbow
The error I'm seeing in the sandbox is different from the previous errors, but it's progress nonetheless. I'm not very familiar with Next.js, so I did some research and I found that Next.js is not natively compatible with ECMAScript module packages. See: vercel/next.js#9607. This is understandable since ES module support (i.e. import
, export
vs. CommonJS require
etc.) is relatively new to NodeJS. The ultimate fix for this will be for Next.js to add support for ES module packages.
There is a work-around though. You can use the next-transpile-modules
to selectively transpile the ES module packages. icecast-metadata-reader
, codec-parser
, and mse-audio-wrapper
are all ES module packages and will need to be transpiled.
Here's the sandbox using the next-transpile-modules
https://codesandbox.io/s/wonderful-firefly-sd9vn?file=/next.config.js
next.config.js
:
const withTM = require("next-transpile-modules")([
"icecast-metadata-player",
"mse-audio-wrapper",
"codec-parser"
]);
module.exports = withTM({
future: {
webpack5: true
}
});
For reference, this is the current error:
Error [ERR_REQUIRE_ESM]: Must use import to load ES Module: /sandbox/node_modules/icecast-metadata-player/src/IcecastMetadataPlayer.js
require() of ES modules is not supported.
require() of /sandbox/node_modules/icecast-metadata-player/src/IcecastMetadataPlayer.js from /sandbox/.next/server/pages/index.js is an ES module file as it is a .js file whose nearest parent package.json contains "type": "module" which defines all .js files in that package scope as ES modules.
Instead rename IcecastMetadataPlayer.js to end in .cjs, change the requiring code to use import(), or remove "type": "module" from /sandbox/node_modules/icecast-metadata-player/package.json.
at Module._extensions..js (internal/modules/cjs/loader.js:1080:13)
at Object.require.extensions.<computed> [as .js] (/sandbox/node_modules/ts-node/src/index.ts:1045:43)
at Module.load (internal/modules/cjs/loader.js:928:32)
at Function.Module._load (internal/modules/cjs/loader.js:769:14)
at Module.require (internal/modules/cjs/loader.js:952:19)
at require (internal/modules/cjs/helpers.js:88:18)
at Object.icecast-metadata-player (/sandbox/.next/server/pages/index.js:33:18)
at __webpack_require__ (/sandbox/.next/server/webpack-runtime.js:33:42)
at eval (webpack-internal:///./src/pages/index.tsx:4:81)
at Object../src/pages/index.tsx (/sandbox/.next/server/pages/index.js:22:1)
at __webpack_require__ (/sandbox/.next/server/webpack-runtime.js:33:42)
at __webpack_exec__ (/sandbox/.next/server/pages/index.js:65:52)
at /sandbox/.next/server/pages/index.js:66:28
at Object.<anonymous> (/sandbox/.next/server/pages/index.js:69:3)
at Module._compile (internal/modules/cjs/loader.js:1063:30)
at Module.m._compile (/sandbox/node_modules/ts-node/src/index.ts:1056:23) {
code: 'ERR_REQUIRE_ESM'
}
Is there any chance the Player will work in Next JS 13? I really need to extract the metadata.
It should work, as long as icecast-metadata-player
is only loaded client side.
Thanks for answering. I'm getting the error in NextJS 13:
Module '"icecast-metadata-player"' has no exported member 'IcecastMetadataPlayer'. Did you mean to use 'import IcecastMetadataPlayer from "icecast-metadata-player"'?ts(2614)
Could you give me an example of how to make it work for the stream: https://servidor18.brlogic.com:7436/live?type=.m3u ?
I don't have any Next JS examples, but you can look at the demo code for plenty of client side examples. If you post a link to your code, I might be able to help, but you should reach out / search within the Next JS community on this as well.
OK thanks. I will try harder for this. I confess that I'm new to Front-End with React and NextJS. I've been able to easily extract metadata in native mobile apps for iOS and Android and with package Just_Audio for Flutter. Thanks for your help.
Trying something similar to the example given above of michaelschufi, I get a warning only when accessing through the browser.
There must be something wrong with your build process. icecast-metadata-js
is supported in Node and browser and it looks like your build is selecting the Node export. You will need to configure your build to use the browser export.
With this code I no longer have the previous errors. Now I am having CORS error. Any suggestion?
Take a look at the readme on CORS and the other git issues tagged CORS.
I gave it a feel in the Readme and added the suggested headers but the error remains. Is there any other configuration to be done in NextJS 13?
happy!
With your example URL it works. It looks like my Stream server doesn't have Cors for all origins.
@oseiasmribeiro the headers should be returned from the icecast server... Make sure to setup icecast to return the required headers:
https://www.icecast.org/docs/icecast-trunk/config_file/#http-headers
@Yahav Thanks!
Audio streaming service BrLogic.com said they cannot release CORS, this is something they are weighing for the future. Do you know of any services that have this where you don't need to set up an Icecast server?
You can setup your own reverse proxy that will append the CORS headers you want.
You will not be able to pull ICY or Ogg metadata without CORS; however, if real-time metadata isn't important to you, you might take a look at icecast-metadata-stats
which can be set up to poll for metadata from the JSON or XML endpoints.
If it were me, I would set up my own Icecast server on something like an Amazon EC2 or Digital Ocean droplet. You would have full control over the configuration, but you also would accept the risk of missconfiguing something, and having your server vulnerable to security exploits.
I understood. Thank you very much. In fact, the metadata, such as the Title, I can say that it is the most important. Although statistics are very useful. Thanks!!!!
Hi
First of all, thank you so much for this library!
It worked flawlessly on a plain HTML/JS page as a proof-of-concept. But unfortunately, it seems to not work out using Next.js. I get the error:
You can reproduce the error here: https://codesandbox.io/s/silly-gates-4wppo?file=/pages/index.js
This is probably because Node.js itself cannot import the player, as seen here: https://codesandbox.io/s/admiring-hugle-64d86?file=/src/index.js
Is there a way to make this work? Maybe by exporting a dummy object, like the HLS player is doing (https://codesandbox.io/s/admiring-hugle-64d86?file=/src/index.js)