mirror of
https://github.com/cgzirim/seek-tune.git
synced 2025-12-17 00:44:19 +00:00
Saving to github after many changes
This commit is contained in:
parent
8d1742fc2b
commit
a1ba649480
31 changed files with 25974 additions and 386 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -19,3 +19,4 @@
|
|||
|
||||
# Go workspace file
|
||||
go.work
|
||||
/songs
|
||||
14
.vscode/settings.json
vendored
Normal file
14
.vscode/settings.json
vendored
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"[python]": {
|
||||
"editor.formatOnSave": true,
|
||||
"editor.defaultFormatter": "charliermarsh.ruff",
|
||||
// "editor.codeActionsOnSave": {
|
||||
// "source.organizeImports": "explicit"
|
||||
// },
|
||||
},
|
||||
"[javascript]": {
|
||||
"editor.formatOnSave": true,
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"python.formatting.provider": "none"
|
||||
}
|
||||
23
client/.gitignore
vendored
Normal file
23
client/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
/.pnp
|
||||
.pnp.js
|
||||
|
||||
# testing
|
||||
/coverage
|
||||
|
||||
# production
|
||||
/build
|
||||
|
||||
# misc
|
||||
.DS_Store
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
70
client/README.md
Normal file
70
client/README.md
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
# Getting Started with Create React App
|
||||
|
||||
This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
|
||||
|
||||
## Available Scripts
|
||||
|
||||
In the project directory, you can run:
|
||||
|
||||
### `npm start`
|
||||
|
||||
Runs the app in the development mode.\
|
||||
Open [http://localhost:3000](http://localhost:3000) to view it in your browser.
|
||||
|
||||
The page will reload when you make changes.\
|
||||
You may also see any lint errors in the console.
|
||||
|
||||
### `npm test`
|
||||
|
||||
Launches the test runner in the interactive watch mode.\
|
||||
See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
|
||||
|
||||
### `npm run build`
|
||||
|
||||
Builds the app for production to the `build` folder.\
|
||||
It correctly bundles React in production mode and optimizes the build for the best performance.
|
||||
|
||||
The build is minified and the filenames include the hashes.\
|
||||
Your app is ready to be deployed!
|
||||
|
||||
See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
|
||||
|
||||
### `npm run eject`
|
||||
|
||||
**Note: this is a one-way operation. Once you `eject`, you can't go back!**
|
||||
|
||||
If you aren't satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
|
||||
|
||||
Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you're on your own.
|
||||
|
||||
You don't have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn't feel obligated to use this feature. However we understand that this tool wouldn't be useful if you couldn't customize it when you are ready for it.
|
||||
|
||||
## Learn More
|
||||
|
||||
You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
|
||||
|
||||
To learn React, check out the [React documentation](https://reactjs.org/).
|
||||
|
||||
### Code Splitting
|
||||
|
||||
This section has moved here: [https://facebook.github.io/create-react-app/docs/code-splitting](https://facebook.github.io/create-react-app/docs/code-splitting)
|
||||
|
||||
### Analyzing the Bundle Size
|
||||
|
||||
This section has moved here: [https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size](https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size)
|
||||
|
||||
### Making a Progressive Web App
|
||||
|
||||
This section has moved here: [https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app](https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app)
|
||||
|
||||
### Advanced Configuration
|
||||
|
||||
This section has moved here: [https://facebook.github.io/create-react-app/docs/advanced-configuration](https://facebook.github.io/create-react-app/docs/advanced-configuration)
|
||||
|
||||
### Deployment
|
||||
|
||||
This section has moved here: [https://facebook.github.io/create-react-app/docs/deployment](https://facebook.github.io/create-react-app/docs/deployment)
|
||||
|
||||
### `npm run build` fails to minify
|
||||
|
||||
This section has moved here: [https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify](https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify)
|
||||
23412
client/package-lock.json
generated
Normal file
23412
client/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
43
client/package.json
Normal file
43
client/package.json
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
{
|
||||
"name": "client",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@testing-library/jest-dom": "^5.17.0",
|
||||
"@testing-library/react": "^13.4.0",
|
||||
"@testing-library/user-event": "^13.5.0",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-scripts": "4.0.3",
|
||||
"simple-peer": "^9.11.1",
|
||||
"socket.io-client": "^2.5.0",
|
||||
"web-vitals": "^2.1.4"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "react-scripts start",
|
||||
"build": "react-scripts build",
|
||||
"test": "react-scripts test",
|
||||
"eject": "react-scripts eject"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": [
|
||||
"react-app",
|
||||
"react-app/jest"
|
||||
]
|
||||
},
|
||||
"browserslist": {
|
||||
"production": [
|
||||
">0.2%",
|
||||
"not dead",
|
||||
"not op_mini all"
|
||||
],
|
||||
"development": [
|
||||
"last 1 chrome version",
|
||||
"last 1 firefox version",
|
||||
"last 1 safari version"
|
||||
]
|
||||
},
|
||||
"devDependencies": {
|
||||
"react-error-overlay": "^6.0.9"
|
||||
}
|
||||
}
|
||||
BIN
client/public/favicon.ico
Normal file
BIN
client/public/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.8 KiB |
43
client/public/index.html
Normal file
43
client/public/index.html
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="theme-color" content="#000000" />
|
||||
<meta
|
||||
name="description"
|
||||
content="Web site created using create-react-app"
|
||||
/>
|
||||
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
|
||||
<!--
|
||||
manifest.json provides metadata used when your web app is installed on a
|
||||
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
|
||||
-->
|
||||
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
|
||||
<!--
|
||||
Notice the use of %PUBLIC_URL% in the tags above.
|
||||
It will be replaced with the URL of the `public` folder during the build.
|
||||
Only files inside the `public` folder can be referenced from the HTML.
|
||||
|
||||
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
|
||||
work correctly both with client-side routing and a non-root public URL.
|
||||
Learn how to configure a non-root public URL by running `npm run build`.
|
||||
-->
|
||||
<title>React App</title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
<div id="root"></div>
|
||||
<!--
|
||||
This HTML file is a template.
|
||||
If you open it directly in the browser, you will see an empty page.
|
||||
|
||||
You can add webfonts, meta tags, or analytics to this file.
|
||||
The build step will place the bundled scripts into the <body> tag.
|
||||
|
||||
To begin the development, run `npm start` or `yarn start`.
|
||||
To create a production bundle, use `npm run build` or `yarn build`.
|
||||
-->
|
||||
</body>
|
||||
</html>
|
||||
BIN
client/public/logo192.png
Normal file
BIN
client/public/logo192.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.2 KiB |
BIN
client/public/logo512.png
Normal file
BIN
client/public/logo512.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 9.4 KiB |
25
client/public/manifest.json
Normal file
25
client/public/manifest.json
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
{
|
||||
"short_name": "React App",
|
||||
"name": "Create React App Sample",
|
||||
"icons": [
|
||||
{
|
||||
"src": "favicon.ico",
|
||||
"sizes": "64x64 32x32 24x24 16x16",
|
||||
"type": "image/x-icon"
|
||||
},
|
||||
{
|
||||
"src": "logo192.png",
|
||||
"type": "image/png",
|
||||
"sizes": "192x192"
|
||||
},
|
||||
{
|
||||
"src": "logo512.png",
|
||||
"type": "image/png",
|
||||
"sizes": "512x512"
|
||||
}
|
||||
],
|
||||
"start_url": ".",
|
||||
"display": "standalone",
|
||||
"theme_color": "#000000",
|
||||
"background_color": "#ffffff"
|
||||
}
|
||||
3
client/public/robots.txt
Normal file
3
client/public/robots.txt
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
# https://www.robotstxt.org/robotstxt.html
|
||||
User-agent: *
|
||||
Disallow:
|
||||
168
client/src/App.js
Normal file
168
client/src/App.js
Normal file
|
|
@ -0,0 +1,168 @@
|
|||
import React, { useEffect, useState } from "react";
|
||||
import Peer from "simple-peer";
|
||||
import io, { managers } from "socket.io-client";
|
||||
import Form from "./Form";
|
||||
|
||||
// const socket = io.connect('http://localhost:5000/');
|
||||
var socket = io("http://localhost:5000/");
|
||||
|
||||
function App() {
|
||||
const [offer, setOffer] = useState();
|
||||
const [stream, setStream] = useState();
|
||||
const [matches, setMatches] = useState([]);
|
||||
const [serverEngaged, setServerEngaged] = useState(false);
|
||||
const [peerConnection, setPeerConnection] = useState();
|
||||
|
||||
// Function to initiate the client peer
|
||||
function initiateClientPeer(stream = null) {
|
||||
const peer = new Peer({
|
||||
initiator: true,
|
||||
trickle: false,
|
||||
stream: stream,
|
||||
});
|
||||
|
||||
let offerHasBeenSet = false;
|
||||
|
||||
peer.on("signal", (data) => {
|
||||
if (!offerHasBeenSet) {
|
||||
console.log("Setting Offer!");
|
||||
setOffer(JSON.stringify(data));
|
||||
offerHasBeenSet = true;
|
||||
}
|
||||
});
|
||||
|
||||
peer.on("close", () => {
|
||||
console.log("CONNECTION CLOSED");
|
||||
});
|
||||
|
||||
peer.on("error", (err) => {
|
||||
console.error("An error occurred:", err);
|
||||
});
|
||||
|
||||
setPeerConnection(peer);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
console.log("Offer updated:", offer);
|
||||
let renegotiated = false;
|
||||
|
||||
if (offer && stream && !renegotiated) {
|
||||
let offerEncoded = btoa(offer);
|
||||
socket.emit("engage", offerEncoded);
|
||||
|
||||
socket.on("serverEngaged", (answer) => {
|
||||
console.log("ServerSDP: ", answer);
|
||||
let decodedAnswer = atob(answer);
|
||||
peerConnection.signal(decodedAnswer);
|
||||
console.log("Engaging Server");
|
||||
setServerEngaged(true);
|
||||
renegotiated = true;
|
||||
});
|
||||
}
|
||||
}, [offer]);
|
||||
|
||||
useEffect(() => {
|
||||
initiateClientPeer();
|
||||
}, []);
|
||||
|
||||
// socket.on("connect", () => {
|
||||
// initiateClientPeer();
|
||||
// });
|
||||
|
||||
socket.on("matches", (matches) => {
|
||||
matches = JSON.parse(matches);
|
||||
setMatches(matches);
|
||||
console.log("Matches: ", matches);
|
||||
});
|
||||
|
||||
socket.on("downloadStatus", (msg) => {
|
||||
console.log("downloadStatus: ", msg);
|
||||
});
|
||||
|
||||
socket.on("albumStat", (msg) => {
|
||||
console.log("Album stat: ", msg);
|
||||
});
|
||||
|
||||
socket.on("playlistStat", (msg) => {
|
||||
console.log("Playlist stat: ", msg);
|
||||
});
|
||||
|
||||
const streamAudio = () => {
|
||||
navigator.mediaDevices
|
||||
.getDisplayMedia({ audio: true })
|
||||
.then((stream) => {
|
||||
peerConnection.addStream(stream);
|
||||
|
||||
// Renegotiate
|
||||
let initOfferEncoded = btoa(offer);
|
||||
socket.emit("initOffer", initOfferEncoded);
|
||||
|
||||
socket.on("initAnswer", (answer) => {
|
||||
let decodedAnswer = atob(answer);
|
||||
peerConnection.signal(decodedAnswer);
|
||||
console.log("Renogotiated");
|
||||
});
|
||||
|
||||
// End of Renegotiation
|
||||
|
||||
peerConnection.on("signal", (data) => {
|
||||
setOffer(JSON.stringify(data));
|
||||
console.log("Offer should be reset");
|
||||
});
|
||||
setStream(stream); // Set the audio stream to state
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error("Error accessing user media:", error);
|
||||
// Handle error
|
||||
});
|
||||
|
||||
if (!offer || !peerConnection) {
|
||||
// If offer is not set, create a new one
|
||||
console.log("NO OFFER. CREATING OFFER");
|
||||
initiateClientPeer(stream);
|
||||
}
|
||||
};
|
||||
|
||||
const disengageServer = () => {
|
||||
peerConnection.destroy();
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="App">
|
||||
<h1>New App</h1>
|
||||
<div>
|
||||
{serverEngaged ? (
|
||||
<button disabled={true}>Listening</button>
|
||||
) : (
|
||||
<button onClick={() => streamAudio()}>Listen</button>
|
||||
)}
|
||||
{serverEngaged && (
|
||||
<button onClick={() => disengageServer()}>Stop Listening</button>
|
||||
)}
|
||||
</div>
|
||||
<Form socket={socket} />
|
||||
<div>
|
||||
{matches.map((match, index) => {
|
||||
const [h, m, s] = match.timestamp.split(":");
|
||||
const timestamp =
|
||||
parseInt(h, 10) * 120 + parseInt(m, 10) * 60 + parseInt(s, 10);
|
||||
|
||||
return (
|
||||
<iframe
|
||||
key={index}
|
||||
width="460"
|
||||
height="284"
|
||||
src={`https://www.youtube.com/embed/${match.youtubeid}?start=${timestamp}`}
|
||||
title={match.songname}
|
||||
frameBorder="0"
|
||||
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
|
||||
allowFullScreen
|
||||
></iframe>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default App;
|
||||
80
client/src/Form.js
Normal file
80
client/src/Form.js
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
import React, { Component } from "react";
|
||||
|
||||
class Form extends Component {
|
||||
initialState = { spotifyUrl: "" };
|
||||
|
||||
state = this.initialState;
|
||||
|
||||
handleChange = (event) => {
|
||||
const { name, value } = event.target;
|
||||
|
||||
this.setState({ [name]: value });
|
||||
};
|
||||
|
||||
submitForm = () => {
|
||||
const { spotifyUrl } = this.state;
|
||||
const { socket } = this.props;
|
||||
if (this.spotifyURLisValid(spotifyUrl) === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
socket.emit("newDownload", spotifyUrl);
|
||||
console.log("newDownload: ", spotifyUrl);
|
||||
this.setState(this.initialState);
|
||||
};
|
||||
|
||||
spotifyURLisValid = (url) => {
|
||||
if (url.length === 0) {
|
||||
console.log("Spotify URL required");
|
||||
return false;
|
||||
}
|
||||
|
||||
const splitURL = url.split("/");
|
||||
if (splitURL.length < 2) {
|
||||
console.log("Invalid Spotify URL format");
|
||||
return false;
|
||||
}
|
||||
|
||||
let spotifyID = splitURL[splitURL.length - 1];
|
||||
if (spotifyID.includes("?")) {
|
||||
spotifyID = spotifyID.split("?")[0];
|
||||
}
|
||||
|
||||
// Check if the Spotify ID is alphanumeric
|
||||
if (!/^[a-zA-Z0-9]+$/.test(spotifyID)) {
|
||||
console.log("Invalid Spotify ID format");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if the Spotify ID is of expected length
|
||||
if (spotifyID.length !== 22) {
|
||||
console.log("Invalid Spotify ID length");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Additional validation logic can be added here
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
render() {
|
||||
const { spotifyUrl } = this.state;
|
||||
|
||||
return (
|
||||
<form>
|
||||
<label htmlFor="spotifyUrl">spotifyUrl</label>
|
||||
<input
|
||||
type="text"
|
||||
name="spotifyUrl"
|
||||
id="spotifyUrl"
|
||||
value={spotifyUrl}
|
||||
placeholder="https://open.spotify.com/.../..."
|
||||
onChange={this.handleChange}
|
||||
/>
|
||||
<input type="button" value="Submit" onClick={this.submitForm} />
|
||||
</form>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default Form;
|
||||
42
client/src/Table.js
Normal file
42
client/src/Table.js
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
|
||||
function TableHeader () {
|
||||
return (
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Job</th>
|
||||
<th>Remove</th>
|
||||
</tr>
|
||||
</thead>
|
||||
)
|
||||
}
|
||||
|
||||
function TableBody (props) {
|
||||
const rows = props.characterData.map((row, index) => {
|
||||
return (
|
||||
<tr key={index}>
|
||||
<td>{row.name}</td>
|
||||
<td>{row.job}</td>
|
||||
<td>
|
||||
<button onClick={() => props.removeCharacter(index)}>Delete</button>
|
||||
</td>
|
||||
</tr>
|
||||
)
|
||||
})
|
||||
|
||||
return <tbody>{rows}</tbody>
|
||||
}
|
||||
|
||||
|
||||
function Table (props) {
|
||||
const { characterData, removeCharacter } = props
|
||||
|
||||
return (
|
||||
<table>
|
||||
<TableHeader />
|
||||
<TableBody characterData={characterData} removeCharacter={removeCharacter} />
|
||||
</table>
|
||||
)
|
||||
}
|
||||
|
||||
export default Table
|
||||
1030
client/src/index.css
Normal file
1030
client/src/index.css
Normal file
File diff suppressed because one or more lines are too long
17
client/src/index.js
Normal file
17
client/src/index.js
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
import React from 'react';
|
||||
import ReactDOM from 'react-dom/client';
|
||||
import './index.css';
|
||||
import App from './App';
|
||||
import reportWebVitals from './reportWebVitals';
|
||||
|
||||
const root = ReactDOM.createRoot(document.getElementById('root'));
|
||||
root.render(
|
||||
<React.StrictMode>
|
||||
<App />
|
||||
</React.StrictMode>
|
||||
);
|
||||
|
||||
// If you want to start measuring performance in your app, pass a function
|
||||
// to log results (for example: reportWebVitals(console.log))
|
||||
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
|
||||
reportWebVitals();
|
||||
13
client/src/reportWebVitals.js
Normal file
13
client/src/reportWebVitals.js
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
const reportWebVitals = onPerfEntry => {
|
||||
if (onPerfEntry && onPerfEntry instanceof Function) {
|
||||
import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
|
||||
getCLS(onPerfEntry);
|
||||
getFID(onPerfEntry);
|
||||
getFCP(onPerfEntry);
|
||||
getLCP(onPerfEntry);
|
||||
getTTFB(onPerfEntry);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
export default reportWebVitals;
|
||||
50
go.mod
50
go.mod
|
|
@ -5,13 +5,21 @@ go 1.21.6
|
|||
require (
|
||||
cloud.google.com/go/compute v1.23.4 // indirect
|
||||
cloud.google.com/go/compute/metadata v0.2.3 // indirect
|
||||
github.com/BharatKalluri/spotifydl v0.1.0 // indirect
|
||||
github.com/adrg/strutil v0.3.1 // indirect
|
||||
github.com/bitly/go-simplejson v0.5.1 // indirect
|
||||
github.com/bogem/id3v2 v1.1.1 // indirect
|
||||
github.com/buger/jsonparser v1.1.1 // indirect
|
||||
github.com/bytedance/sonic v1.9.1 // indirect
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/dlclark/regexp2 v1.10.0 // indirect
|
||||
github.com/dop251/goja v0.0.0-20231027120936-b396bb4c349d // indirect
|
||||
github.com/dlclark/regexp2 v1.11.0 // indirect
|
||||
github.com/dop251/goja v0.0.0-20240220182346-e401ed450204 // indirect
|
||||
github.com/fatih/color v1.16.0 // indirect
|
||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
|
||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||
github.com/gin-gonic/gin v1.9.1 // indirect
|
||||
github.com/giorgisio/goav v0.1.0 // indirect
|
||||
github.com/go-audio/audio v1.0.0 // indirect
|
||||
github.com/go-audio/riff v1.0.0 // indirect
|
||||
|
|
@ -20,21 +28,37 @@ require (
|
|||
github.com/go-fingerprint/gochroma v0.0.0-20211004000611-a294aa5ccab6 // indirect
|
||||
github.com/go-logr/logr v1.4.1 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/go-sourcemap/sourcemap v2.1.3+incompatible // indirect
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/go-playground/validator/v10 v10.14.0 // indirect
|
||||
github.com/go-sourcemap/sourcemap v2.1.4+incompatible // indirect
|
||||
github.com/goccy/go-json v0.10.2 // indirect
|
||||
github.com/gofrs/uuid v4.0.0+incompatible // indirect
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||
github.com/golang/protobuf v1.5.3 // indirect
|
||||
github.com/golang/snappy v0.0.4 // indirect
|
||||
github.com/google/pprof v0.0.0-20231101202521-4ca4178f5c7a // indirect
|
||||
github.com/gomodule/redigo v1.8.4 // indirect
|
||||
github.com/google/pprof v0.0.0-20240320155624-b11c3daa6f07 // indirect
|
||||
github.com/google/s2a-go v0.1.7 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect
|
||||
github.com/googleapis/gax-go/v2 v2.12.1 // indirect
|
||||
github.com/googollee/go-socket.io v1.7.0 // indirect
|
||||
github.com/gorilla/websocket v1.4.2 // indirect
|
||||
github.com/hajimehoshi/go-mp3 v0.3.4 // indirect
|
||||
github.com/kkdai/youtube/v2 v2.10.0 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/kkdai/youtube/v2 v2.10.1 // indirect
|
||||
github.com/klauspost/compress v1.17.6 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.4 // indirect
|
||||
github.com/leodido/go-urn v1.2.4 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mjibson/go-dsp v0.0.0-20180508042940-11479a337f12 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/montanaflynn/stats v0.7.1 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.1.1 // indirect
|
||||
github.com/pion/datachannel v1.5.5 // indirect
|
||||
github.com/pion/dtls/v2 v2.2.10 // indirect
|
||||
github.com/pion/ice/v3 v3.0.3 // indirect
|
||||
|
|
@ -54,26 +78,32 @@ require (
|
|||
github.com/pion/webrtc/v4 v4.0.0-beta.9 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
github.com/raitonoberu/ytmusic v0.0.0-20220927155833-3d1de71caa11 // indirect
|
||||
github.com/stretchr/objx v0.5.0 // indirect
|
||||
github.com/stretchr/testify v1.8.4 // indirect
|
||||
github.com/spf13/cobra v1.8.0 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/stretchr/objx v0.5.2 // indirect
|
||||
github.com/stretchr/testify v1.9.0 // indirect
|
||||
github.com/tidwall/gjson v1.17.1 // indirect
|
||||
github.com/tidwall/match v1.1.1 // indirect
|
||||
github.com/tidwall/pretty v1.2.0 // indirect
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||
github.com/ugorji/go/codec v1.2.11 // indirect
|
||||
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
|
||||
github.com/xdg-go/scram v1.1.2 // indirect
|
||||
github.com/xdg-go/stringprep v1.0.4 // indirect
|
||||
github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a // indirect
|
||||
github.com/zmb3/spotify v0.0.0-20191028153142-869e03dbd8b0 // indirect
|
||||
go.mongodb.org/mongo-driver v1.14.0 // indirect
|
||||
go.opencensus.io v0.24.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.48.0 // indirect
|
||||
go.opentelemetry.io/otel v1.23.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.23.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.23.0 // indirect
|
||||
golang.org/x/crypto v0.19.0 // indirect
|
||||
golang.org/x/net v0.21.0 // indirect
|
||||
golang.org/x/arch v0.3.0 // indirect
|
||||
golang.org/x/crypto v0.21.0 // indirect
|
||||
golang.org/x/net v0.22.0 // indirect
|
||||
golang.org/x/oauth2 v0.17.0 // indirect
|
||||
golang.org/x/sync v0.6.0 // indirect
|
||||
golang.org/x/sys v0.17.0 // indirect
|
||||
golang.org/x/sys v0.18.0 // indirect
|
||||
golang.org/x/text v0.14.0 // indirect
|
||||
google.golang.org/api v0.166.0 // indirect
|
||||
google.golang.org/appengine v1.6.8 // indirect
|
||||
|
|
|
|||
156
go.sum
156
go.sum
|
|
@ -1,21 +1,41 @@
|
|||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
|
||||
cloud.google.com/go/compute v1.23.4 h1:EBT9Nw4q3zyE7G45Wvv3MzolIrCJEuHys5muLY0wvAw=
|
||||
cloud.google.com/go/compute v1.23.4/go.mod h1:/EJMj55asU6kAFnuZET8zqgwgJ9FvXWXOkkfQZa4ioI=
|
||||
cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY=
|
||||
cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA=
|
||||
github.com/BharatKalluri/spotifydl v0.1.0 h1:BzaukOeFenfmSFFSfHPUIF839+lIPr7G8MRaw0Q1b2Q=
|
||||
github.com/BharatKalluri/spotifydl v0.1.0/go.mod h1:NBlYj+lhmo/TaL4w1c1nsVcU2/prqcqvKYrrsEUYcjA=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/ZekeLu/go-mp3 v0.3.5-pre h1:D2Ttzfp/ZazLKVryN0Hv0kyuKbej0ofU1Cagwu+e8zA=
|
||||
github.com/ZekeLu/go-mp3 v0.3.5-pre/go.mod h1:fRtZraRFcWb0pu7ok0LqyFhCUrPeMsGRSVop0eemFmo=
|
||||
github.com/adrg/strutil v0.3.1 h1:OLvSS7CSJO8lBii4YmBt8jiK9QOtB9CzCzwl4Ic/Fz4=
|
||||
github.com/adrg/strutil v0.3.1/go.mod h1:8h90y18QLrs11IBffcGX3NW/GFBXCMcNg4M7H6MspPA=
|
||||
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
|
||||
github.com/bitly/go-simplejson v0.5.1 h1:xgwPbetQScXt1gh9BmoJ6j9JMr3TElvuIyjR8pgdoow=
|
||||
github.com/bitly/go-simplejson v0.5.1/go.mod h1:YOPVLzCfwK14b4Sff3oP1AmGhI9T9Vsg84etUnlyp+Q=
|
||||
github.com/bogem/id3v2 v1.1.1 h1:FnjS2vytMeEb39tOMG09uz852MaEccA2A3asRM3XxbE=
|
||||
github.com/bogem/id3v2 v1.1.1/go.mod h1:D1rDm80qF/ocBU+Ik8U4RKnwMq/oNkkB8vGcnrlMJmM=
|
||||
github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs=
|
||||
github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0=
|
||||
github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
|
||||
github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s=
|
||||
github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY=
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams=
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
|
||||
github.com/chzyer/logex v1.2.0/go.mod h1:9+9sk7u7pGNWYMkh0hdiL++6OeibzJccyQU4p4MedaY=
|
||||
github.com/chzyer/readline v1.5.0/go.mod h1:x22KAscuvRqlLoK9CsoYsmxoXZMMFVyOl86cAH8qUic=
|
||||
github.com/chzyer/test v0.0.0-20210722231415-061457976a23/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
|
||||
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
|
|
@ -26,9 +46,13 @@ github.com/dlclark/regexp2 v1.4.1-0.20201116162257-a2a8dda75c91/go.mod h1:2pZnwu
|
|||
github.com/dlclark/regexp2 v1.7.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
|
||||
github.com/dlclark/regexp2 v1.10.0 h1:+/GIL799phkJqYW+3YbOd8LCcbHzT0Pbo8zl70MHsq0=
|
||||
github.com/dlclark/regexp2 v1.10.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
|
||||
github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI=
|
||||
github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
|
||||
github.com/dop251/goja v0.0.0-20211022113120-dc8c55024d06/go.mod h1:R9ET47fwRVRPZnOGvHxxhuZcbrMCuiqOz3Rlrh4KSnk=
|
||||
github.com/dop251/goja v0.0.0-20231027120936-b396bb4c349d h1:wi6jN5LVt/ljaBG4ue79Ekzb12QfJ52L9Q98tl8SWhw=
|
||||
github.com/dop251/goja v0.0.0-20231027120936-b396bb4c349d/go.mod h1:QMWlm50DNe14hD7t24KEqZuUdC9sOTy8W6XbCU1mlw4=
|
||||
github.com/dop251/goja v0.0.0-20240220182346-e401ed450204 h1:O7I1iuzEA7SG+dK8ocOBSlYAA9jBUmCYl/Qa7ey7JAM=
|
||||
github.com/dop251/goja v0.0.0-20240220182346-e401ed450204/go.mod h1:QMWlm50DNe14hD7t24KEqZuUdC9sOTy8W6XbCU1mlw4=
|
||||
github.com/dop251/goja_nodejs v0.0.0-20210225215109-d91c329300e7/go.mod h1:hn7BA7c8pLvoGndExHudxTDKZ84Pyvv+90pbBjbTz0Y=
|
||||
github.com/dop251/goja_nodejs v0.0.0-20211022123610-8dd9abb0616d/go.mod h1:DngW8aVqWbuLRMHItjPUyqdj+HWPvnQe8V8y1nDpIbM=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
|
|
@ -41,6 +65,12 @@ github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2
|
|||
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU=
|
||||
github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA=
|
||||
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
|
||||
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
||||
github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg=
|
||||
github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU=
|
||||
github.com/giorgisio/goav v0.1.0 h1:ZyfG3NfX7PMSimv4ulhmnQJf/XeHpMdGCn+afRmY5Oc=
|
||||
github.com/giorgisio/goav v0.1.0/go.mod h1:RtH8HyxLRLU1iY0pjfhWBKRhnbsnmfoI+FxMwb5bfEo=
|
||||
github.com/go-audio/audio v1.0.0 h1:zS9vebldgbQqktK4H0lUqWrG8P0NxCJVqcj7ZpNnwd4=
|
||||
|
|
@ -58,15 +88,29 @@ github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
|
|||
github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
||||
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
||||
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
||||
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||
github.com/go-playground/validator/v10 v10.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg/+t63MyGU2n5js=
|
||||
github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
|
||||
github.com/go-sourcemap/sourcemap v2.1.3+incompatible h1:W1iEw64niKVGogNgBN3ePyLFfuisuzeidWPMPWmECqU=
|
||||
github.com/go-sourcemap/sourcemap v2.1.3+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg=
|
||||
github.com/go-sourcemap/sourcemap v2.1.4+incompatible h1:a+iTbH5auLKxaNwQFg0B+TCYl6lbukKPc7b5x0n1s6Q=
|
||||
github.com/go-sourcemap/sourcemap v2.1.4+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg=
|
||||
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
|
||||
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
|
||||
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||
github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw=
|
||||
github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||
|
|
@ -82,6 +126,9 @@ github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg
|
|||
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
|
||||
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/gomodule/redigo v1.8.4 h1:Z5JUg94HMTR1XpwBaSH4vq3+PNSIykBLxMdglbw10gg=
|
||||
github.com/gomodule/redigo v1.8.4/go.mod h1:P9dn9mFrCBvWhGE1wpxx6fgq7BAeLBk+UUUzlpkBYO0=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
|
|
@ -89,9 +136,14 @@ github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
|
|||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/pprof v0.0.0-20230207041349-798e818bf904/go.mod h1:uglQLonpP8qtYCYyzA+8c/9qtqgA3qsXGYqCPKARAFg=
|
||||
github.com/google/pprof v0.0.0-20231101202521-4ca4178f5c7a h1:fEBsGL/sjAuJrgah5XqmmYsTLzJp/TO9Lhy39gkverk=
|
||||
github.com/google/pprof v0.0.0-20231101202521-4ca4178f5c7a/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik=
|
||||
github.com/google/pprof v0.0.0-20240320155624-b11c3daa6f07 h1:57oOH2Mu5Nw16KnZAVLdlUjmPH/TSYCKTJgG0OVfX0Y=
|
||||
github.com/google/pprof v0.0.0-20240320155624-b11c3daa6f07/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw=
|
||||
github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o=
|
||||
github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw=
|
||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
|
|
@ -99,30 +151,62 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
|||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.2 h1:Vie5ybvEvT75RniqhfFxPRy3Bf7vr3h0cechB90XaQs=
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0=
|
||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||
github.com/googleapis/gax-go/v2 v2.12.1 h1:9F8GV9r9ztXyAi00gsMQHNoF51xPZm8uj1dpYt2ZETM=
|
||||
github.com/googleapis/gax-go/v2 v2.12.1/go.mod h1:61M8vcyyXR2kqKFxKrfA22jaA8JGF7Dc8App1U3H6jc=
|
||||
github.com/googollee/go-socket.io v1.7.0 h1:ODcQSAvVIPvKozXtUGuJDV3pLwdpBLDs1Uoq/QHIlY8=
|
||||
github.com/googollee/go-socket.io v1.7.0/go.mod h1:0vGP8/dXR9SZUMMD4+xxaGo/lohOw3YWMh2WRiWeKxg=
|
||||
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
|
||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/gosuri/uilive v0.0.0-20170323041506-ac356e6e42cd/go.mod h1:qkLSc0A5EXSP6B04TrN4oQoxqFI7A8XvoXSlJi8cwk8=
|
||||
github.com/gosuri/uiprogress v0.0.0-20170224063937-d0567a9d84a1/go.mod h1:C1RTYn4Sc7iEyf6j8ft5dyoZ4212h8G1ol9QQluh5+0=
|
||||
github.com/hajimehoshi/go-mp3 v0.3.4 h1:NUP7pBYH8OguP4diaTZ9wJbUbk3tC0KlfzsEpWmYj68=
|
||||
github.com/hajimehoshi/go-mp3 v0.3.4/go.mod h1:fRtZraRFcWb0pu7ok0LqyFhCUrPeMsGRSVop0eemFmo=
|
||||
github.com/hajimehoshi/oto/v2 v2.3.1/go.mod h1:seWLbgHH7AyUMYKfKYT9pg7PhUu9/SisyJvNTT+ASQo=
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20220319035150-800ac71e25c2/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w=
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||
github.com/kkdai/youtube/v2 v2.10.0 h1:s8gSWo3AxIafK560XwDVnha9aPXp3N2HQAh1x81R5Og=
|
||||
github.com/kkdai/youtube/v2 v2.10.0/go.mod h1:H5MLUXiXYuovcEhQT/uZf7BC/syIbAJlDKCDsG+WDsU=
|
||||
github.com/kkdai/youtube/v2 v2.10.1 h1:jdPho4R7VxWoRi9Wx4ULMq4+hlzSVOXxh4Zh83f2F9M=
|
||||
github.com/kkdai/youtube/v2 v2.10.1/go.mod h1:qL8JZv7Q1IoDs4nnaL51o/hmITXEIvyCIXopB0oqgVM=
|
||||
github.com/klauspost/compress v1.17.6 h1:60eq2E/jlfwQXtvZEeBUYADs+BwKBWURIY+Gj2eRGjI=
|
||||
github.com/klauspost/compress v1.17.6/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM=
|
||||
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk=
|
||||
github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q=
|
||||
github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4=
|
||||
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/mjibson/go-dsp v0.0.0-20180508042940-11479a337f12 h1:dd7vnTDfjtwCETZDrRe+GPYNLA1jBtbZeyfyE8eZCyk=
|
||||
github.com/mjibson/go-dsp v0.0.0-20180508042940-11479a337f12/go.mod h1:i/KKcxEWEO8Yyl11DYafRPKOPVYTrhxiTRigjtEEXZU=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/montanaflynn/stats v0.7.1 h1:etflOAAHORrCC44V+aR6Ftzort912ZU+YLiSTuV8eaE=
|
||||
github.com/montanaflynn/stats v0.7.1/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow=
|
||||
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
||||
|
|
@ -134,6 +218,12 @@ github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042
|
|||
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
||||
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
||||
github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
|
||||
github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=
|
||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4=
|
||||
github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
|
||||
github.com/pelletier/go-toml/v2 v2.1.1 h1:LWAJwfNvjQZCFIDKWYQaM62NcYeYViCmWIwmOStowAI=
|
||||
github.com/pelletier/go-toml/v2 v2.1.1/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
|
||||
github.com/pion/datachannel v1.5.5 h1:10ef4kwdjije+M9d7Xm9im2Y3O6A6ccQb0zcqZcJew8=
|
||||
github.com/pion/datachannel v1.5.5/go.mod h1:iMz+lECmfdCMqFRhXhcA/219B0SQlbpoR2V118yimL0=
|
||||
github.com/pion/dtls/v2 v2.2.7/go.mod h1:8WiMkebSHFD0T+dIU+UeBaoV7kDhOW5oDCzZ7WZ/F9s=
|
||||
|
|
@ -184,36 +274,65 @@ github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:
|
|||
github.com/raitonoberu/ytmusic v0.0.0-20220927155833-3d1de71caa11 h1:jpddPIqdeF+TxOT1Zzd1u+k9AiVT4XJX/VuyZUnrgZE=
|
||||
github.com/raitonoberu/ytmusic v0.0.0-20220927155833-3d1de71caa11/go.mod h1:hgP4hPl8kmhAaMjuaxxqKnHa7yA9UkXw4KY97XLyjRs=
|
||||
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw=
|
||||
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
||||
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
|
||||
github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0=
|
||||
github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho=
|
||||
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
||||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
|
||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/tidwall/gjson v1.17.1 h1:wlYEnwqAHgzmhNUFfw7Xalt2JzQvsMx2Se4PcoFCT/U=
|
||||
github.com/tidwall/gjson v1.17.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
|
||||
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
|
||||
github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
|
||||
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
||||
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
|
||||
github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU=
|
||||
github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
||||
github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=
|
||||
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
|
||||
github.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY=
|
||||
github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4=
|
||||
github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8=
|
||||
github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM=
|
||||
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
||||
github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a h1:fZHgsYlfvtyqToslyjUt3VOPF4J7aK/3MPcK7xp3PDk=
|
||||
github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a/go.mod h1:ul22v+Nro/R083muKhosV54bj5niojjWZvU8xrevuH4=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
github.com/zmb3/spotify v0.0.0-20191028153142-869e03dbd8b0 h1:MVLklg1SWVS2rvK1NDXmy04rgjfq7dCnyncqZfPWL+A=
|
||||
github.com/zmb3/spotify v0.0.0-20191028153142-869e03dbd8b0/go.mod h1:pHsWAmY9PfX7i/uwPZkmWrebc8JbK8FppKbvyevwzSU=
|
||||
go.mongodb.org/mongo-driver v1.14.0 h1:P98w8egYRjYe3XDjxhYJagTokP/H6HzlsnojRgZRd80=
|
||||
go.mongodb.org/mongo-driver v1.14.0/go.mod h1:Vzb0Mk/pa7e6cWw85R4F/endUC3u0U9jGcNU603k65c=
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
|
||||
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.48.0 h1:doUP+ExOpH3spVTLS0FcWGLnQrPct/hD/bCPbDRUEAU=
|
||||
|
|
@ -224,6 +343,10 @@ go.opentelemetry.io/otel/metric v1.23.0 h1:pazkx7ss4LFVVYSxYew7L5I6qvLXHA0Ap2pwV
|
|||
go.opentelemetry.io/otel/metric v1.23.0/go.mod h1:MqUW2X2a6Q8RN96E2/nqNoT+z9BSms20Jb7Bbp+HiTo=
|
||||
go.opentelemetry.io/otel/trace v1.23.0 h1:37Ik5Ib7xfYVb4V1UtnT97T1jI+AoIYkJyPkuL4iJgI=
|
||||
go.opentelemetry.io/otel/trace v1.23.0/go.mod h1:GSGTbIClEsuZrGIzoEHqsVfxgn5UkggkflQwDScNUsk=
|
||||
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
||||
golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k=
|
||||
golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
||||
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
|
|
@ -235,19 +358,25 @@ golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc=
|
|||
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
|
||||
golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo=
|
||||
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
||||
golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA=
|
||||
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
|
|
@ -264,11 +393,18 @@ golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo=
|
|||
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
|
||||
golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
|
||||
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
||||
golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc=
|
||||
golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20191122200657-5d9234df094c/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.17.0 h1:6m3ZPmLEFdVxKKWnKq4VqZ60gutO35zm+zrAHVmHyDQ=
|
||||
golang.org/x/oauth2 v0.17.0/go.mod h1:OzPDGQiuQMguemayvdylqddI7qcD9lnSDb+1FiwQ5HA=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
|
|
@ -277,8 +413,10 @@ golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
|
|||
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
|
|
@ -290,6 +428,7 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220712014510-0a85c31ab51e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
|
|
@ -304,6 +443,8 @@ golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
|
|||
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y=
|
||||
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
|
||||
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
|
|
@ -313,6 +454,8 @@ golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
|||
golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU=
|
||||
golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
|
|
@ -323,10 +466,13 @@ golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
|||
golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
|
|
@ -336,19 +482,26 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T
|
|||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
||||
google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||
google.golang.org/api v0.166.0 h1:6m4NUwrZYhAaVIHZWxaKjw1L1vNAjtMwORmKRyEEo24=
|
||||
google.golang.org/api v0.166.0/go.mod h1:4FcBc686KFi7QI/U51/2GKKevfZMpM17sCdibqe/bSA=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM=
|
||||
google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||
google.golang.org/genproto v0.0.0-20240205150955-31a09d347014 h1:g/4bk7P6TPMkAUbUhquq98xey1slwvuVJPosdBqYJlU=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240213162025-012b6fc9bca9 h1:hZB7eLIaYlW9qXRfCq/qDaPdbeY3757uARz5Vvfv+cY=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240213162025-012b6fc9bca9/go.mod h1:YUWgXUFRPfoYK1IHMuxH5K6nPEXSCzIMljnQ59lLRCk=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
|
|
@ -383,4 +536,7 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C
|
|||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
|
||||
|
|
|
|||
33
imain.go
33
imain.go
|
|
@ -34,28 +34,39 @@ func matchSong(songPath string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func imain() {
|
||||
func main() {
|
||||
// Example usage
|
||||
// Open the MP3 file
|
||||
// mp3FilePath := "spotifydown.com - These Are The Days.mp3"
|
||||
// signal.Process_and_SaveSong(mp3FilePath, "These Are The Days", "lauren Daigle")
|
||||
|
||||
// https://open.spotify.com/track/3vnKyPnHMunE1bMXYQHFHU?si=34a43de5712c4331 - heaven has come
|
||||
// https://open.spotify.com/track/6h2vZPWSWsRJ0ps91epUgT?si=7ac5c26041014ea4 - What's going on
|
||||
// https://open.spotify.com/track/7zwSMMJkrRJNvxFO9w42nA?si=fa7cef0f7bd14904 - we raise a sound Nosa and 121SELAH
|
||||
// https://open.spotify.com/track/52WA7y6ACfdHbzIii6M9iA?si=8aa26d3974394645 - these are the days
|
||||
// https://open.spotify.com/track/3ddxe0WYUpNPtSnHgQOad5?si=8c1665c5b1384e9e - I still have faith in you
|
||||
|
||||
// spotify.DlSingleTrack("https://open.spotify.com/track/3vnKyPnHMunE1bMXYQHFHU?si=34a43de5712c4331",
|
||||
// "/home/chigozirim/Documents/my-docs/song-recognition/songs/")
|
||||
|
||||
// spotify.DlSingleTrack("https://open.spotify.com/track/7zwSMMJkrRJNvxFO9w42nA?si=fa7cef0f7bd14904",
|
||||
// "/home/chigozirim/Documents/my-docs/song-recognition/songs/")
|
||||
|
||||
// spotify.DlSingleTrack("https://open.spotify.com/track/52WA7y6ACfdHbzIii6M9iA?si=8aa26d3974394645",
|
||||
// "/home/chigozirim/Documents/my-docs/song-recognition/songs/")
|
||||
|
||||
spotify.DlPlaylist("https://open.spotify.com/playlist/7EAqBCOVkDZcbccjxZmgjp?si=bbc07260fb784861",
|
||||
spotify.DlSingleTrack("https://open.spotify.com/track/3vnKyPnHMunE1bMXYQHFHU?si=34a43de5712c4331",
|
||||
"/home/chigozirim/Documents/my-docs/song-recognition/songs/")
|
||||
|
||||
spotify.DlSingleTrack("https://open.spotify.com/track/6h2vZPWSWsRJ0ps91epUgT?si=7ac5c26041014ea4",
|
||||
"/home/chigozirim/Documents/my-docs/song-recognition/songs/")
|
||||
|
||||
spotify.DlSingleTrack("https://open.spotify.com/track/7zwSMMJkrRJNvxFO9w42nA?si=fa7cef0f7bd14904",
|
||||
"/home/chigozirim/Documents/my-docs/song-recognition/songs/")
|
||||
|
||||
spotify.DlSingleTrack("https://open.spotify.com/track/52WA7y6ACfdHbzIii6M9iA?si=8aa26d3974394645",
|
||||
"/home/chigozirim/Documents/my-docs/song-recognition/songs/")
|
||||
spotify.DlSingleTrack("https://open.spotify.com/track/3ddxe0WYUpNPtSnHgQOad5?si=8c1665c5b1384e9e",
|
||||
"/home/chigozirim/Documents/my-docs/song-recognition/songs/")
|
||||
|
||||
// spotify.DlPlaylist("https://open.spotify.com/playlist/7EAqBCOVkDZcbccjxZmgjp?si=bbc07260fb784861",
|
||||
// "/home/chigozirim/Documents/my-docs/song-recognition/songs/")
|
||||
|
||||
// AJR Mix
|
||||
// spotify.DlPlaylist("https://open.spotify.com/playlist/37i9dQZF1EIZjJcbmXVBoA?si=35d7d4ba237147cf",
|
||||
// "/home/chigozirim/Documents/my-docs/song-recognition/songs/")
|
||||
|
||||
// err := matchSong("/home/chigozirim/Documents/my-docs/song-recognition/songs/We Raise A Sound - Nosa.m4a")
|
||||
// if err != nil {
|
||||
// fmt.Println("error matching song: ", err)
|
||||
|
|
|
|||
42
main.go
42
main.go
|
|
@ -22,7 +22,6 @@ import (
|
|||
"song-recognition/signal"
|
||||
|
||||
"github.com/pion/webrtc/v4/pkg/media"
|
||||
"github.com/pion/webrtc/v4/pkg/media/ivfwriter"
|
||||
"github.com/pion/webrtc/v4/pkg/media/oggwriter"
|
||||
)
|
||||
|
||||
|
|
@ -69,7 +68,7 @@ func saveToBytes(track *webrtc.TrackRemote) ([]byte, error) {
|
|||
return audioData, nil
|
||||
}
|
||||
|
||||
func matchSampleAudio(track *webrtc.TrackRemote) (string, error) {
|
||||
func MatchSampleAudio(track *webrtc.TrackRemote) (string, error) {
|
||||
// Use time.After to stop after 15 seconds
|
||||
stop := time.After(50 * time.Second)
|
||||
|
||||
|
|
@ -127,13 +126,7 @@ func main() {
|
|||
m := &webrtc.MediaEngine{}
|
||||
|
||||
// Setup the codecs you want to use.
|
||||
// We'll use a VP8 and Opus but you can also define your own
|
||||
if err := m.RegisterCodec(webrtc.RTPCodecParameters{
|
||||
RTPCodecCapability: webrtc.RTPCodecCapability{MimeType: webrtc.MimeTypeVP8, ClockRate: 90000, Channels: 0, SDPFmtpLine: "", RTCPFeedback: nil},
|
||||
PayloadType: 96,
|
||||
}, webrtc.RTPCodecTypeVideo); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
// We'll use Opus, but you can also define your own
|
||||
if err := m.RegisterCodec(webrtc.RTPCodecParameters{
|
||||
RTPCodecCapability: webrtc.RTPCodecCapability{MimeType: webrtc.MimeTypeOpus, ClockRate: 44100, Channels: 1, SDPFmtpLine: "", RTCPFeedback: nil},
|
||||
PayloadType: 111,
|
||||
|
|
@ -142,15 +135,11 @@ func main() {
|
|||
}
|
||||
|
||||
// Create a InterceptorRegistry. This is the user configurable RTP/RTCP Pipeline.
|
||||
// This provides NACKs, RTCP Reports and other features. If you use `webrtc.NewPeerConnection`
|
||||
// this is enabled by default. If you are manually managing You MUST create a InterceptorRegistry
|
||||
// for each PeerConnection.
|
||||
// This provides NACKs, RTCP Reports and other features.
|
||||
i := &interceptor.Registry{}
|
||||
|
||||
// Register a intervalpli factory
|
||||
// This interceptor sends a PLI every 3 seconds. A PLI causes a video keyframe to be generated by the sender.
|
||||
// This makes our video seekable and more error resilent, but at a cost of lower picture quality and higher bitrates
|
||||
// A real world application should process incoming RTCP packets from viewers and forward them to senders
|
||||
// This interceptor sends a PLI every 3 seconds. A PLI causes a keyframe to be generated by the sender.
|
||||
intervalPliFactory, err := intervalpli.NewReceiverInterceptor()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
|
|
@ -180,35 +169,24 @@ func main() {
|
|||
panic(err)
|
||||
}
|
||||
|
||||
// Allow us to receive 1 audio track, and 1 video track
|
||||
// Allow us to receive 1 audio track
|
||||
if _, err = peerConnection.AddTransceiverFromKind(webrtc.RTPCodecTypeAudio); err != nil {
|
||||
panic(err)
|
||||
} else if _, err = peerConnection.AddTransceiverFromKind(webrtc.RTPCodecTypeVideo); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// oggFile, err := oggwriter.New("output.ogg", 48000, 2)
|
||||
// Create an Ogg file for audio output
|
||||
oggFile, err := oggwriter.New("output.ogg", 44100, 1)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
ivfFile, err := ivfwriter.New("output.ivf")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Set a handler for when a new remote track starts, this handler saves buffers to disk as
|
||||
// an ivf file, since we could have multiple video tracks we provide a counter.
|
||||
// In your application this is where you would handle/process video
|
||||
// an Ogg file.
|
||||
peerConnection.OnTrack(func(track *webrtc.TrackRemote, receiver *webrtc.RTPReceiver) {
|
||||
codec := track.Codec()
|
||||
if strings.EqualFold(codec.MimeType, webrtc.MimeTypeOpus) {
|
||||
fmt.Println("Got Opus track, saving to disk as output.opus (48 kHz, 2 channels)")
|
||||
// matchSampleAudio(track)
|
||||
fmt.Println("Got Opus track, saving to disk as output.opus (44.1 kHz, 1 channel)")
|
||||
saveToDisk(oggFile, track)
|
||||
} else if strings.EqualFold(codec.MimeType, webrtc.MimeTypeVP8) {
|
||||
fmt.Println("Got VP8 track, saving to disk as output.ivf")
|
||||
saveToDisk(ivfFile, track)
|
||||
}
|
||||
})
|
||||
|
||||
|
|
@ -224,10 +202,6 @@ func main() {
|
|||
panic(closeErr)
|
||||
}
|
||||
|
||||
if closeErr := ivfFile.Close(); closeErr != nil {
|
||||
panic(closeErr)
|
||||
}
|
||||
|
||||
fmt.Println("Done writing media files")
|
||||
|
||||
// Gracefully shutdown the peer connection
|
||||
|
|
|
|||
224
server.go
Normal file
224
server.go
Normal file
|
|
@ -0,0 +1,224 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"song-recognition/signal"
|
||||
"song-recognition/spotify"
|
||||
"strings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/pion/webrtc/v4"
|
||||
"github.com/pion/webrtc/v4/pkg/media/oggwriter"
|
||||
|
||||
socketio "github.com/googollee/go-socket.io"
|
||||
)
|
||||
|
||||
const (
|
||||
tmpSongDir = "/home/chigozirim/Documents/my-docs/song-recognition/songs/"
|
||||
)
|
||||
|
||||
func GinMiddleware(allowOrigin string) gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
c.Writer.Header().Set("Access-Control-Allow-Origin", allowOrigin)
|
||||
c.Writer.Header().Set("Access-Control-Allow-Credentials", "true")
|
||||
c.Writer.Header().Set("Access-Control-Allow-Methods", "POST, OPTIONS, GET, PUT, DELETE")
|
||||
c.Writer.Header().Set("Access-Control-Allow-Headers", "Accept, Authorization, Content-Type, Content-Length, X-CSRF-Token, Token, session, Origin, Host, Connection, Accept-Encoding, Accept-Language, X-Requested-With")
|
||||
|
||||
if c.Request.Method == http.MethodOptions {
|
||||
c.AbortWithStatus(http.StatusNoContent)
|
||||
return
|
||||
}
|
||||
|
||||
c.Request.Header.Del("Origin")
|
||||
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
router := gin.New()
|
||||
|
||||
server := socketio.NewServer(nil)
|
||||
|
||||
server.OnConnect("/", func(s socketio.Conn) error {
|
||||
s.SetContext("")
|
||||
log.Println("CONNECTED: ", s.ID())
|
||||
return nil
|
||||
})
|
||||
|
||||
server.OnEvent("/", "initOffer", func(s socketio.Conn, initEncodedOffer string) {
|
||||
log.Println("initOffer: ", initEncodedOffer)
|
||||
|
||||
peerConnection := signal.SetupWebRTC(initEncodedOffer)
|
||||
s.Emit("initAnswer", signal.Encode(*peerConnection.LocalDescription()))
|
||||
})
|
||||
|
||||
server.OnEvent("/", "engage", func(s socketio.Conn, encodedOffer string) {
|
||||
log.Println("engage: ", encodedOffer)
|
||||
|
||||
peerConnection := signal.SetupWebRTC(encodedOffer)
|
||||
|
||||
// Allow us to receive 1 audio track
|
||||
if _, err := peerConnection.AddTransceiverFromKind(webrtc.RTPCodecTypeAudio); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Set a handler for when a new remote track starts, this handler saves buffers to disk as
|
||||
// an Ogg file.
|
||||
oggFile, err := oggwriter.New("output.ogg", 44100, 1)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
peerConnection.OnTrack(func(track *webrtc.TrackRemote, receiver *webrtc.RTPReceiver) {
|
||||
codec := track.Codec()
|
||||
if strings.EqualFold(codec.MimeType, webrtc.MimeTypeOpus) {
|
||||
fmt.Println("Got Opus track, saving to disk as output.opus (44.1 kHz, 1 channel)")
|
||||
// signal.SaveToDisk(oggFile, track)
|
||||
// TODO turn match to json here
|
||||
matches, err := signal.MatchSampleAudio(track)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
jsonData, err := json.Marshal(matches[:5])
|
||||
if err != nil {
|
||||
fmt.Println("Log error: ", err)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Println(string(jsonData))
|
||||
|
||||
s.Emit("matches", string(jsonData))
|
||||
peerConnection.Close()
|
||||
}
|
||||
})
|
||||
|
||||
// Set the handler for ICE connection state
|
||||
// This will notify you when the peer has connected/disconnected
|
||||
peerConnection.OnICEConnectionStateChange(func(connectionState webrtc.ICEConnectionState) {
|
||||
fmt.Printf("Connection State has changed %s \n", connectionState.String())
|
||||
|
||||
if connectionState == webrtc.ICEConnectionStateConnected {
|
||||
fmt.Println("Ctrl+C the remote client to stop the demo")
|
||||
} else if connectionState == webrtc.ICEConnectionStateFailed || connectionState == webrtc.ICEConnectionStateClosed {
|
||||
if closeErr := oggFile.Close(); closeErr != nil {
|
||||
panic(closeErr)
|
||||
}
|
||||
|
||||
fmt.Println("Done writing media files")
|
||||
|
||||
// Gracefully shutdown the peer connection
|
||||
if closeErr := peerConnection.Close(); closeErr != nil {
|
||||
panic(closeErr)
|
||||
}
|
||||
|
||||
// os.Exit(0)
|
||||
}
|
||||
})
|
||||
|
||||
// Emit answer in base64
|
||||
s.Emit("serverEngaged", signal.Encode(*peerConnection.LocalDescription()))
|
||||
})
|
||||
|
||||
server.OnEvent("/", "newDownload", func(socket socketio.Conn, spotifyURL string) {
|
||||
if len(spotifyURL) == 0 {
|
||||
fmt.Println("=> Spotify URL required.")
|
||||
return
|
||||
}
|
||||
|
||||
splitURL := strings.Split(spotifyURL, "/")
|
||||
|
||||
if len(splitURL) < 2 {
|
||||
fmt.Println("=> Please enter the url copied from the spotify client.")
|
||||
return
|
||||
}
|
||||
|
||||
spotifyID := splitURL[len(splitURL)-1]
|
||||
if strings.Contains(spotifyID, "?") {
|
||||
spotifyID = strings.Split(spotifyID, "?")[0]
|
||||
}
|
||||
|
||||
if strings.Contains(spotifyURL, "album") {
|
||||
tracksInAlbum, err := spotify.AlbumInfo(spotifyURL)
|
||||
if err != nil {
|
||||
fmt.Println("log error: ", err)
|
||||
return
|
||||
}
|
||||
|
||||
socket.Emit("albumStat", fmt.Sprintf("%v songs found in album.", len(tracksInAlbum)))
|
||||
|
||||
totalTracksDownloaded, err := spotify.DlAlbum(spotifyURL, tmpSongDir)
|
||||
if err != nil {
|
||||
socket.Emit("downloadStatus", fmt.Sprintf("Failed to download album."))
|
||||
return
|
||||
}
|
||||
|
||||
socket.Emit("downloadStatus", fmt.Sprintf("%d songs downloaded from album", totalTracksDownloaded))
|
||||
|
||||
} else if strings.Contains(spotifyURL, "playlist") {
|
||||
tracksInPL, err := spotify.PlaylistInfo(spotifyURL)
|
||||
if err != nil {
|
||||
fmt.Println("log error: ", err)
|
||||
return
|
||||
}
|
||||
|
||||
socket.Emit("playlistStat", fmt.Sprintf("%v songs found in playlist.", len(tracksInPL)))
|
||||
|
||||
totalTracksDownloaded, err := spotify.DlPlaylist(spotifyURL, tmpSongDir)
|
||||
if err != nil {
|
||||
fmt.Println("log errorr: ", err)
|
||||
socket.Emit("downloadStatus", fmt.Sprintf("Failed to download playlist."))
|
||||
return
|
||||
}
|
||||
|
||||
socket.Emit("downloadStatus", fmt.Sprintf("%d songs downloaded from playlist", totalTracksDownloaded))
|
||||
|
||||
} else if strings.Contains(spotifyURL, "track") {
|
||||
// check if track already exist
|
||||
trackInfo, err := spotify.TrackInfo(spotifyURL)
|
||||
if err != nil {
|
||||
fmt.Println("log error: ", err)
|
||||
return
|
||||
}
|
||||
|
||||
err = spotify.DlSingleTrack(spotifyURL, tmpSongDir)
|
||||
if err != nil {
|
||||
socket.Emit("downloadStatus", fmt.Sprintf("Failed to download '%s' by '%s'", trackInfo.Title, trackInfo.Artist))
|
||||
return
|
||||
}
|
||||
|
||||
socket.Emit("downloadStatus", fmt.Sprintf("'%s' by '%s' was downloaded", trackInfo.Title, trackInfo.Artist))
|
||||
|
||||
} else {
|
||||
fmt.Println("=> Only Spotify Album/Playlist/Track URL's are supported.")
|
||||
return
|
||||
}
|
||||
})
|
||||
|
||||
server.OnError("/", func(s socketio.Conn, e error) {
|
||||
log.Println("meet error:", e)
|
||||
})
|
||||
|
||||
server.OnDisconnect("/", func(s socketio.Conn, reason string) {
|
||||
log.Println("closed", reason)
|
||||
})
|
||||
|
||||
go func() {
|
||||
if err := server.Serve(); err != nil {
|
||||
log.Fatalf("socketio listen error: %s\n", err)
|
||||
}
|
||||
}()
|
||||
defer server.Close()
|
||||
|
||||
router.Use(GinMiddleware("http://localhost:3000"))
|
||||
router.GET("/socket.io/*any", gin.WrapH(server))
|
||||
router.POST("/socket.io/*any", gin.WrapH(server))
|
||||
|
||||
if err := router.Run(":5000"); err != nil {
|
||||
log.Fatal("failed run app: ", err)
|
||||
}
|
||||
}
|
||||
139
shazam/shazam.go
139
shazam/shazam.go
|
|
@ -10,71 +10,75 @@ import (
|
|||
"song-recognition/utils"
|
||||
"sort"
|
||||
"time"
|
||||
|
||||
"github.com/mjibson/go-dsp/fft"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
)
|
||||
|
||||
// Constants
|
||||
const (
|
||||
chunkSize = 4096 // 4KB
|
||||
hopSize = 128
|
||||
fuzzFactor = 2
|
||||
bitDepth = 2
|
||||
channels = 1
|
||||
samplingRate = 44100
|
||||
)
|
||||
|
||||
// AudioInfo contains details about the audio data.
|
||||
type AudioInfo struct {
|
||||
SongName string
|
||||
SongArtist string
|
||||
BitDepth int
|
||||
Channels int
|
||||
SamplingRate int
|
||||
TimeStamp string // TimeStamp for the chunk
|
||||
type ChunkTag struct {
|
||||
SongName string
|
||||
SongArtist string
|
||||
YouTubeID string
|
||||
TimeStamp string
|
||||
}
|
||||
|
||||
func Match(sampleAudio []byte) (string, error) {
|
||||
func Match(sampleAudio []byte) ([]primitive.M, error) {
|
||||
sampleChunks := Chunkify(sampleAudio)
|
||||
chunkFingerprints, _ := FingerprintChunks(sampleChunks, nil)
|
||||
|
||||
db, err := utils.NewDbClient()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error connecting to DB: %d", err)
|
||||
return nil, fmt.Errorf("error connecting to DB: %d", err)
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
var results = make(map[string][]string)
|
||||
var chunkTags = make(map[string]primitive.M)
|
||||
var songsTimestamps = make(map[string][]string)
|
||||
for _, chunkfgp := range chunkFingerprints {
|
||||
listOfChunkData, err := db.GetChunkData(chunkfgp)
|
||||
listOfChunkTags, err := db.GetChunkData(chunkfgp)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("error getting chunk data with fingerpring %d: %v", chunkfgp, err)
|
||||
return nil, fmt.Errorf("error getting chunk data with fingerprint %d: %v", chunkfgp, err)
|
||||
}
|
||||
|
||||
for _, chunkData := range listOfChunkData {
|
||||
timeStamp := fmt.Sprint(chunkData["timestamp"])
|
||||
songKey := fmt.Sprintf("%s by %s", chunkData["songname"], chunkData["songartist"])
|
||||
for _, chunkTag := range listOfChunkTags {
|
||||
timeStamp := fmt.Sprint(chunkTag["timestamp"])
|
||||
songKey := fmt.Sprintf("%s by %s", chunkTag["songname"], chunkTag["songartist"])
|
||||
|
||||
if results[songKey] == nil {
|
||||
results[songKey] = []string{timeStamp}
|
||||
if songsTimestamps[songKey] == nil {
|
||||
songsTimestamps[songKey] = []string{timeStamp}
|
||||
chunkTags[songKey] = chunkTag
|
||||
} else {
|
||||
results[songKey] = append(results[songKey], timeStamp)
|
||||
songsTimestamps[songKey] = append(songsTimestamps[songKey], timeStamp)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Println("Results: ", results)
|
||||
|
||||
maxMatchCount := 0
|
||||
var maxMatch string
|
||||
|
||||
for songKey, timestamps := range results {
|
||||
matches := make(map[string][]int)
|
||||
|
||||
for songKey, timestamps := range songsTimestamps {
|
||||
differences, err := timeDifference(timestamps)
|
||||
if err != nil && err.Error() == "insufficient timestamps" {
|
||||
continue
|
||||
} else if err != nil {
|
||||
return "", err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fmt.Printf("%s DIFFERENCES: %d\n", songKey, differences)
|
||||
if len(differences) >= 2 {
|
||||
matches[songKey] = differences
|
||||
if len(differences) > maxMatchCount {
|
||||
maxMatchCount = len(differences)
|
||||
maxMatch = songKey
|
||||
|
|
@ -82,8 +86,41 @@ func Match(sampleAudio []byte) (string, error) {
|
|||
}
|
||||
}
|
||||
|
||||
sortedChunkTags := sortMatchesByTimeDifference(matches, chunkTags)
|
||||
|
||||
fmt.Println("SORTED CHUNK TAGS: ", sortedChunkTags)
|
||||
fmt.Println("MATCHES: ", matches)
|
||||
fmt.Println("MATCH: ", maxMatch)
|
||||
return "", nil
|
||||
fmt.Println()
|
||||
return sortedChunkTags, nil
|
||||
}
|
||||
|
||||
func sortMatchesByTimeDifference(matches map[string][]int, chunkTags map[string]primitive.M) []primitive.M {
|
||||
type songDifferences struct {
|
||||
songKey string
|
||||
differences []int
|
||||
sum int
|
||||
}
|
||||
|
||||
var kvPairs []songDifferences
|
||||
for songKey, differences := range matches {
|
||||
sum := 0
|
||||
for _, difference := range differences {
|
||||
sum += difference
|
||||
}
|
||||
kvPairs = append(kvPairs, songDifferences{songKey, differences, sum})
|
||||
}
|
||||
|
||||
sort.Slice(kvPairs, func(i, j int) bool {
|
||||
return kvPairs[i].sum > kvPairs[j].sum
|
||||
})
|
||||
|
||||
var sortedChunkTags []primitive.M
|
||||
for _, pair := range kvPairs {
|
||||
sortedChunkTags = append(sortedChunkTags, chunkTags[pair.songKey])
|
||||
}
|
||||
|
||||
return sortedChunkTags
|
||||
}
|
||||
|
||||
func timeDifference(timestamps []string) ([]int, error) {
|
||||
|
|
@ -105,8 +142,7 @@ func timeDifference(timestamps []string) ([]int, error) {
|
|||
timestampsInSeconds[i] = (hours * 3600) + (minutes * 60) + seconds
|
||||
}
|
||||
|
||||
sort.Ints(timestampsInSeconds)
|
||||
fmt.Println("timeStampsInSeconds: ", timestampsInSeconds)
|
||||
// sort.Ints(timestampsInSeconds)
|
||||
|
||||
differences := []int{}
|
||||
|
||||
|
|
@ -124,7 +160,6 @@ func timeDifference(timestamps []string) ([]int, error) {
|
|||
// Chunkify divides the input audio signal into chunks and calculates the Short-Time Fourier Transform (STFT) for each chunk.
|
||||
// The function returns a 2D slice containing the STFT coefficients for each chunk.
|
||||
func Chunkify(audio []byte) [][]complex128 {
|
||||
const hopSize = 32
|
||||
numWindows := len(audio) / (chunkSize - hopSize)
|
||||
chunks := make([][]complex128, numWindows)
|
||||
|
||||
|
|
@ -149,7 +184,8 @@ func Chunkify(audio []byte) [][]complex128 {
|
|||
}
|
||||
|
||||
// Compute FFT
|
||||
chunks[i] = Fft(chunk)
|
||||
// chunks[i] = Fft(chunk)
|
||||
chunks[i] = fft.FFT(chunk)
|
||||
}
|
||||
|
||||
return chunks
|
||||
|
|
@ -157,17 +193,19 @@ func Chunkify(audio []byte) [][]complex128 {
|
|||
|
||||
// FingerprintChunks processes a collection of audio data represented as chunks of complex numbers and
|
||||
// generates fingerprints for each chunk based on the magnitude of frequency components within specific frequency ranges.
|
||||
func FingerprintChunks(chunks [][]complex128, audioInfo *AudioInfo) ([]int64, map[int64]AudioInfo) {
|
||||
func FingerprintChunks(chunks [][]complex128, chunkTag *ChunkTag) ([]int64, map[int64]ChunkTag) {
|
||||
var fingerprintList []int64
|
||||
fingerprintMap := make(map[int64]AudioInfo)
|
||||
fingerprintMap := make(map[int64]ChunkTag)
|
||||
|
||||
var bytesPerSecond, chunksPerSecond int
|
||||
var chunksPerSecond int
|
||||
var chunkCount int
|
||||
var chunkTime time.Time
|
||||
|
||||
if audioInfo != nil {
|
||||
bytesPerSecond = (samplingRate * bitDepth * channels) / 8
|
||||
chunksPerSecond = bytesPerSecond / chunkSize
|
||||
if chunkTag != nil {
|
||||
// bytesPerSecond = (samplingRate * bitDepth * channels) / 8
|
||||
chunksPerSecond = (chunkSize - hopSize) / samplingRate
|
||||
chunksPerSecond = 9
|
||||
fmt.Println("CHUNKS PER SECOND: ", chunksPerSecond)
|
||||
// if chunkSize == 4096 {
|
||||
// chunksPerSecond = 10
|
||||
// }
|
||||
|
|
@ -176,7 +214,7 @@ func FingerprintChunks(chunks [][]complex128, audioInfo *AudioInfo) ([]int64, ma
|
|||
}
|
||||
|
||||
for _, chunk := range chunks {
|
||||
if audioInfo != nil {
|
||||
if chunkTag != nil {
|
||||
chunkCount++
|
||||
if chunkCount == chunksPerSecond {
|
||||
chunkCount = 0
|
||||
|
|
@ -218,13 +256,22 @@ func FingerprintChunks(chunks [][]complex128, audioInfo *AudioInfo) ([]int64, ma
|
|||
int64(chunkMags["250-500"]),
|
||||
int64(chunkMags["500-2000"]),
|
||||
int64(chunkMags["2000-4000"])}
|
||||
key := hash1(points[:])
|
||||
// key := hash1(points[:])
|
||||
// fmt.Printf("%s: %v\n", fingerprint, key)
|
||||
|
||||
if audioInfo != nil {
|
||||
newAudioInfo := *audioInfo
|
||||
newAudioInfo.TimeStamp = chunkTime.Format("15:04:05")
|
||||
fingerprintMap[key] = newAudioInfo
|
||||
// points := [6]int64{
|
||||
// int64(chunkMags["20-60"]),
|
||||
// int64(chunkMags["60-250"]),
|
||||
// int64(chunkMags["250-500"]),
|
||||
// int64(chunkMags["500-2000"]),
|
||||
// int64(chunkMags["2000-4000"]),
|
||||
// int64(chunkMags["4000-8000"])}
|
||||
key := hash(points[:])
|
||||
|
||||
if chunkTag != nil {
|
||||
newSampleTag := *chunkTag
|
||||
newSampleTag.TimeStamp = chunkTime.Format("15:04:05")
|
||||
fingerprintMap[key] = newSampleTag
|
||||
} else {
|
||||
fingerprintList = append(fingerprintList, key)
|
||||
}
|
||||
|
|
@ -234,15 +281,11 @@ func FingerprintChunks(chunks [][]complex128, audioInfo *AudioInfo) ([]int64, ma
|
|||
}
|
||||
|
||||
func hash(values []int64) int64 {
|
||||
if len(values) != 7 {
|
||||
return 0 // Handle invalid input length
|
||||
}
|
||||
|
||||
weight := 100
|
||||
var result int64
|
||||
for i := 0; i < len(values); i++ {
|
||||
roundedValue := values[i] - (values[i] % fuzzFactor)
|
||||
weight := int64(math.Pow10(len(values) - i - 1))
|
||||
result += roundedValue * weight
|
||||
for _, value := range values {
|
||||
result += (value - (value % fuzzFactor)) * int64(weight)
|
||||
weight = weight * weight
|
||||
}
|
||||
|
||||
return result
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
207
signal/webrtc.go
Normal file
207
signal/webrtc.go
Normal file
|
|
@ -0,0 +1,207 @@
|
|||
// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
//go:build !js
|
||||
// +build !js
|
||||
|
||||
// save-to-disk is a simple application that shows how to record your webcam/microphone using Pion WebRTC and save VP8/Opus to disk.
|
||||
package signal
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"time"
|
||||
|
||||
"github.com/pion/interceptor"
|
||||
"github.com/pion/interceptor/pkg/intervalpli"
|
||||
"github.com/pion/webrtc/v4"
|
||||
"go.mongodb.org/mongo-driver/bson/primitive"
|
||||
|
||||
"song-recognition/shazam"
|
||||
|
||||
"github.com/pion/webrtc/v4/pkg/media"
|
||||
)
|
||||
|
||||
func SaveToDisk(i media.Writer, track *webrtc.TrackRemote) {
|
||||
defer func() {
|
||||
if err := i.Close(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}()
|
||||
|
||||
for {
|
||||
rtpPacket, _, err := track.ReadRTP()
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
if err := i.WriteRTP(rtpPacket); err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func SaveToBytes(track *webrtc.TrackRemote) ([]byte, error) {
|
||||
var audioData []byte
|
||||
|
||||
for {
|
||||
rtpPacket, _, err := track.ReadRTP()
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Extract audio payload from RTP packet
|
||||
payload := rtpPacket.Payload
|
||||
|
||||
// Append audio payload to audioData
|
||||
audioData = append(audioData, payload...)
|
||||
// fmt.Println("ByteArray: ", audioData)
|
||||
}
|
||||
|
||||
return audioData, nil
|
||||
}
|
||||
|
||||
func MatchSampleAudio(track *webrtc.TrackRemote) ([]primitive.M, error) {
|
||||
// Use time.After to stop after 15 seconds
|
||||
stop := time.After(50 * time.Second)
|
||||
|
||||
// Use a ticker to process sampleAudio every 2 seconds
|
||||
ticker := time.NewTicker(2 * time.Second)
|
||||
defer ticker.Stop()
|
||||
|
||||
var sampleAudio []byte
|
||||
var matches []primitive.M
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
// Process sampleAudio every 2 seconds
|
||||
if len(sampleAudio) > 0 {
|
||||
matchess, err := shazam.Match(sampleAudio)
|
||||
matches = matchess
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Reset sampleAudio for fresh input
|
||||
// sampleAudio = nil
|
||||
// if len(matches) > 0 {
|
||||
// fmt.Println("FOUND A MATCH! - ", matches)
|
||||
// jsonData, err := json.Marshal(matches)
|
||||
// if err != nil {
|
||||
// fmt.Println(err)
|
||||
// return "", nil
|
||||
// }
|
||||
// return string(jsonData), nil
|
||||
// }
|
||||
}
|
||||
case <-stop:
|
||||
// Stop after 15 seconds
|
||||
fmt.Println("Stopped after 15 seconds")
|
||||
return matches, nil
|
||||
default:
|
||||
// Read RTP packets and accumulate sampleAudio
|
||||
rtpPacket, _, err := track.ReadRTP()
|
||||
if err != nil {
|
||||
if err != io.EOF {
|
||||
return nil, fmt.Errorf("error reading RTP packet: %d", err)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Extract audio payload from RTP packet
|
||||
payload := rtpPacket.Payload
|
||||
|
||||
sampleAudio = append(sampleAudio, payload...)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// nolint:gocognit
|
||||
func SetupWebRTC(encodedOffer string) *webrtc.PeerConnection {
|
||||
// Everything below is the Pion WebRTC API! Thanks for using it ❤️.
|
||||
|
||||
// Create a MediaEngine object to configure the supported codec
|
||||
m := &webrtc.MediaEngine{}
|
||||
|
||||
// Setup the codecs you want to use.
|
||||
// We'll use Opus, but you can also define your own
|
||||
if err := m.RegisterCodec(webrtc.RTPCodecParameters{
|
||||
RTPCodecCapability: webrtc.RTPCodecCapability{MimeType: webrtc.MimeTypeOpus, ClockRate: 44100, Channels: 1, SDPFmtpLine: "", RTCPFeedback: nil},
|
||||
PayloadType: 111,
|
||||
}, webrtc.RTPCodecTypeAudio); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Create a InterceptorRegistry. This is the user configurable RTP/RTCP Pipeline.
|
||||
// This provides NACKs, RTCP Reports and other features.
|
||||
i := &interceptor.Registry{}
|
||||
|
||||
// Register a intervalpli factory
|
||||
// This interceptor sends a PLI every 3 seconds. A PLI causes a keyframe to be generated by the sender.
|
||||
intervalPliFactory, err := intervalpli.NewReceiverInterceptor()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
i.Add(intervalPliFactory)
|
||||
|
||||
// Use the default set of Interceptors
|
||||
if err = webrtc.RegisterDefaultInterceptors(m, i); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Create the API object with the MediaEngine
|
||||
api := webrtc.NewAPI(webrtc.WithMediaEngine(m), webrtc.WithInterceptorRegistry(i))
|
||||
|
||||
// Prepare the configuration
|
||||
config := webrtc.Configuration{
|
||||
ICEServers: []webrtc.ICEServer{
|
||||
{
|
||||
URLs: []string{"stun:stun.l.google.com:19302"},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
// Create a new RTCPeerConnection
|
||||
peerConnection, err := api.NewPeerConnection(config)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Wait for the offer to be pasted
|
||||
offer := webrtc.SessionDescription{}
|
||||
Decode(encodedOffer, &offer)
|
||||
|
||||
// Set the remote SessionDescription
|
||||
err = peerConnection.SetRemoteDescription(offer)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Create answer
|
||||
answer, err := peerConnection.CreateAnswer(nil)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Create channel that is blocked until ICE Gathering is complete
|
||||
gatherComplete := webrtc.GatheringCompletePromise(peerConnection)
|
||||
|
||||
// Sets the LocalDescription, and starts our UDP listeners
|
||||
err = peerConnection.SetLocalDescription(answer)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Block until ICE Gathering is complete, disabling trickle ICE
|
||||
// we do this because we only can exchange one signaling message
|
||||
// in a production application you should exchange ICE Candidates via OnICECandidate
|
||||
<-gatherComplete
|
||||
|
||||
return peerConnection
|
||||
}
|
||||
|
|
@ -15,6 +15,8 @@ import (
|
|||
"sync"
|
||||
"time"
|
||||
|
||||
// "song-recognition/youtube"
|
||||
|
||||
"github.com/fatih/color"
|
||||
"github.com/kkdai/youtube/v2"
|
||||
)
|
||||
|
|
@ -32,7 +34,7 @@ func DlSingleTrack(url, savePath string) error {
|
|||
track := []Track{*trackInfo}
|
||||
|
||||
fmt.Println("Now, downloading track...")
|
||||
err = dlTrack(track, savePath)
|
||||
_, err = dlTrack(track, savePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -40,41 +42,42 @@ func DlSingleTrack(url, savePath string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func DlPlaylist(url, savePath string) error {
|
||||
func DlPlaylist(url, savePath string) (int, error) {
|
||||
tracks, err := PlaylistInfo(url)
|
||||
if err != nil {
|
||||
return err
|
||||
return 0, err
|
||||
}
|
||||
|
||||
time.Sleep(1 * time.Second)
|
||||
fmt.Println("Now, downloading playlist...")
|
||||
err = dlTrack(tracks, savePath)
|
||||
totalTracksDownloaded, err := dlTrack(tracks, savePath)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return err
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return nil
|
||||
return totalTracksDownloaded, nil
|
||||
}
|
||||
|
||||
func dlAlbum(url, savePath string) error {
|
||||
func DlAlbum(url, savePath string) (int, error) {
|
||||
tracks, err := AlbumInfo(url)
|
||||
if err != nil {
|
||||
return err
|
||||
return 0, err
|
||||
}
|
||||
|
||||
time.Sleep(1 * time.Second)
|
||||
fmt.Println("Now, downloading album...")
|
||||
err = dlTrack(tracks, savePath)
|
||||
totalTracksDownloaded, err := dlTrack(tracks, savePath)
|
||||
if err != nil {
|
||||
return err
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return nil
|
||||
return totalTracksDownloaded, nil
|
||||
}
|
||||
|
||||
func dlTrack(tracks []Track, path string) error {
|
||||
func dlTrack(tracks []Track, path string) (int, error) {
|
||||
var wg sync.WaitGroup
|
||||
var downloadedTracks []string
|
||||
var totalTracks int
|
||||
results := make(chan int, len(tracks))
|
||||
numCPUs := runtime.NumCPU()
|
||||
|
|
@ -90,27 +93,30 @@ func dlTrack(tracks []Track, path string) error {
|
|||
}()
|
||||
|
||||
trackCopy := &Track{
|
||||
Title: track.Title,
|
||||
Artist: track.Artist,
|
||||
Album: track.Album,
|
||||
Album: track.Album,
|
||||
Artist: track.Artist,
|
||||
Artists: track.Artists,
|
||||
Duration: track.Duration,
|
||||
Title: track.Title,
|
||||
}
|
||||
|
||||
id, err := VideoID(*trackCopy)
|
||||
if id == "" || err != nil {
|
||||
// id1, err := VideoID(*trackCopy)
|
||||
ytID, err := GetYoutubeId(*trackCopy)
|
||||
if ytID == "" || err != nil {
|
||||
yellow.Printf("Error (1): '%s' by '%s' could not be downloaded\n", trackCopy.Title, trackCopy.Artist)
|
||||
return
|
||||
}
|
||||
|
||||
trackCopy.Title, trackCopy.Artist = correctFilename(trackCopy.Title, trackCopy.Artist)
|
||||
err = getAudio(id, path, trackCopy.Title, trackCopy.Artist)
|
||||
err = getAudio(ytID, path, trackCopy.Title, trackCopy.Artist)
|
||||
if err != nil {
|
||||
yellow.Printf("Error (2): '%s' by '%s' could not be downloaded\n", trackCopy.Title, trackCopy.Artist)
|
||||
yellow.Printf("Error (2): '%s' by '%s' could not be downloaded: %s\n", trackCopy.Title, trackCopy.Artist, err)
|
||||
return
|
||||
}
|
||||
// Process and save audio
|
||||
filename := fmt.Sprintf("%s - %s.m4a", trackCopy.Title, trackCopy.Artist)
|
||||
route := filepath.Join(path, filename)
|
||||
err = processAndSaveSong(route, trackCopy.Title, trackCopy.Artist)
|
||||
err = processAndSaveSong(route, trackCopy.Title, trackCopy.Artist, ytID)
|
||||
if err != nil {
|
||||
yellow.Println("Error processing audio: ", err)
|
||||
}
|
||||
|
|
@ -129,6 +135,7 @@ func dlTrack(tracks []Track, path string) error {
|
|||
}
|
||||
|
||||
fmt.Printf("'%s' by '%s' was downloaded\n", track.Title, track.Artist)
|
||||
downloadedTracks = append(downloadedTracks, fmt.Sprintf("%s, %s", track.Title, track.Artist))
|
||||
results <- 1
|
||||
}(t)
|
||||
}
|
||||
|
|
@ -138,12 +145,12 @@ func dlTrack(tracks []Track, path string) error {
|
|||
close(results)
|
||||
}()
|
||||
|
||||
for result := range results {
|
||||
totalTracks += result
|
||||
for range results {
|
||||
totalTracks++
|
||||
}
|
||||
|
||||
fmt.Println("Total tracks downloaded:", totalTracks)
|
||||
return nil
|
||||
return totalTracks, nil
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -171,8 +178,7 @@ func getAudio(id, path, title, artist string) error {
|
|||
return err
|
||||
}
|
||||
if songExists {
|
||||
fmt.Println("Song exists: ", songKey)
|
||||
return nil
|
||||
return fmt.Errorf("song exists")
|
||||
}
|
||||
|
||||
client := youtube.Client{}
|
||||
|
|
@ -302,7 +308,7 @@ func correctFilename(title, artist string) (string, string) {
|
|||
return title, artist
|
||||
}
|
||||
|
||||
func processAndSaveSong(m4aFile, songName, songArtist string) error {
|
||||
func processAndSaveSong(m4aFile, songName, songArtist, ytID string) error {
|
||||
db, err := utils.NewDbClient()
|
||||
if err != nil {
|
||||
return fmt.Errorf("error connecting to DB: %d", err)
|
||||
|
|
@ -313,16 +319,16 @@ func processAndSaveSong(m4aFile, songName, songArtist string) error {
|
|||
songKey := fmt.Sprintf("%s - %s", songName, songArtist)
|
||||
songExists, err := db.SongExists(songKey)
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("error checking if song exists: %v", err)
|
||||
}
|
||||
if songExists {
|
||||
fmt.Println("Song exists: ", songKey)
|
||||
return fmt.Errorf("error querying existing songs: %v", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Convert M4A file to mono
|
||||
m4aFileMono := strings.TrimSuffix(m4aFile, filepath.Ext(m4aFile)) + "_mono.m4a"
|
||||
defer os.Remove(m4aFileMono) // Ensure the temporary output file is deleted
|
||||
// defer os.Remove(m4aFileMono)
|
||||
audioBytes, err := ConvertM4aToMono(m4aFile, m4aFileMono)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error converting M4A file to mono: %v", err)
|
||||
|
|
@ -339,20 +345,17 @@ func processAndSaveSong(m4aFile, songName, songArtist string) error {
|
|||
lines := strings.Split(string(output), "\n")
|
||||
// bitDepth, _ := strconv.Atoi(strings.TrimSpace(lines[1]))
|
||||
sampleRate, _ := strconv.Atoi(strings.TrimSpace(lines[0]))
|
||||
fmt.Printf("SAMPLE RATE for %s: %v", songName, sampleRate)
|
||||
|
||||
audioInfo := shazam.AudioInfo{
|
||||
SongName: songName,
|
||||
SongArtist: songArtist,
|
||||
BitDepth: 2,
|
||||
Channels: 1,
|
||||
SamplingRate: sampleRate,
|
||||
chunkTag := shazam.ChunkTag{
|
||||
SongName: songName,
|
||||
SongArtist: songArtist,
|
||||
YouTubeID: ytID,
|
||||
}
|
||||
|
||||
fmt.Println("AUDIO INFO: ", audioInfo)
|
||||
|
||||
// Calculate fingerprints
|
||||
chunks := shazam.Chunkify(audioBytes)
|
||||
_, fingerprints := shazam.FingerprintChunks(chunks, &audioInfo)
|
||||
_, fingerprints := shazam.FingerprintChunks(chunks, &chunkTag)
|
||||
|
||||
// Save fingerprints to MongoDB
|
||||
for fgp, chunkData := range fingerprints {
|
||||
|
|
|
|||
|
|
@ -20,6 +20,8 @@ type ResourceEndpoint struct {
|
|||
|
||||
type Track struct {
|
||||
Title, Artist, Album string
|
||||
Artists []string
|
||||
Duration int
|
||||
}
|
||||
|
||||
const (
|
||||
|
|
@ -106,11 +108,11 @@ func TrackInfo(url string) (*Track, error) {
|
|||
return nil, fmt.Errorf("received non-200 status code: %d", statusCode)
|
||||
}
|
||||
|
||||
track := &Track{
|
||||
Title: gjson.Get(jsonResponse, "data.trackUnion.name").String(),
|
||||
Artist: gjson.Get(jsonResponse, "data.trackUnion.firstArtist.items.0.profile.name").String(),
|
||||
Album: gjson.Get(jsonResponse, "data.trackUnion.albumOfTrack.name").String(),
|
||||
}
|
||||
// track := &Track{
|
||||
// Title: gjson.Get(jsonResponse, "data.trackUnion.name").String(),
|
||||
// Artist: gjson.Get(jsonResponse, "data.trackUnion.firstArtist.items.0.profile.name").String(),
|
||||
// Album: gjson.Get(jsonResponse, "data.trackUnion.albumOfTrack.name").String(),
|
||||
// }
|
||||
|
||||
var allArtists []string
|
||||
|
||||
|
|
@ -128,6 +130,14 @@ func TrackInfo(url string) (*Track, error) {
|
|||
}
|
||||
}
|
||||
|
||||
track := &Track{
|
||||
Title: gjson.Get(jsonResponse, "data.trackUnion.name").String(),
|
||||
Artist: gjson.Get(jsonResponse, "data.trackUnion.firstArtist.items.0.profile.name").String(),
|
||||
Artists: allArtists,
|
||||
Duration: int(gjson.Get(jsonResponse, "data.trackUnion.duration.totalMilliseconds").Int()),
|
||||
Album: gjson.Get(jsonResponse, "data.trackUnion.albumOfTrack.name").String(),
|
||||
}
|
||||
|
||||
fmt.Println("ARTISTS: ", allArtists)
|
||||
fmt.Println("TRACK: ", track)
|
||||
|
||||
|
|
|
|||
179
spotify/utils.go
179
spotify/utils.go
|
|
@ -1,24 +1,12 @@
|
|||
package spotify
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/url"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/signal"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"unicode"
|
||||
|
||||
"golang.org/x/text/runes"
|
||||
"golang.org/x/text/transform"
|
||||
"golang.org/x/text/unicode/norm"
|
||||
)
|
||||
|
||||
func EncodeParam(s string) string {
|
||||
|
|
@ -38,91 +26,6 @@ func ToLowerCase(s string) string {
|
|||
return result
|
||||
}
|
||||
|
||||
func isPathValid(path string) bool {
|
||||
dir, err := os.Stat(path)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return false
|
||||
}
|
||||
|
||||
if !dir.IsDir() {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func NewDir(path string) (string, error) {
|
||||
if !isPathValid(path) {
|
||||
return "", errors.New("invalid path")
|
||||
}
|
||||
|
||||
dirName := "YourMusic"
|
||||
fullPath := filepath.Join(path, dirName)
|
||||
|
||||
if runtime.GOOS == "windows" {
|
||||
fullPath = fullPath + "\\"
|
||||
} else {
|
||||
fullPath = fullPath + "/"
|
||||
}
|
||||
|
||||
err := os.Mkdir(fullPath, 0700)
|
||||
if err != nil {
|
||||
fmt.Sprintln("Error: %w", err)
|
||||
return "", err
|
||||
}
|
||||
|
||||
return fullPath, nil
|
||||
}
|
||||
|
||||
func ToZip(dir, zipPath string) error {
|
||||
zipFile, err := os.Create(zipPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer zipFile.Close()
|
||||
|
||||
zipWriter := zip.NewWriter(zipFile)
|
||||
defer zipWriter.Close()
|
||||
|
||||
err = filepath.Walk(dir, func(filePath string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if filePath == dir {
|
||||
return nil
|
||||
}
|
||||
|
||||
header, err := zip.FileInfoHeader(info)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
header.Name, _ = filepath.Rel(dir, filePath)
|
||||
|
||||
writer, err := zipWriter.CreateHeader(header)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !info.IsDir() {
|
||||
file, err := os.Open(filePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
_, err = io.Copy(writer, file)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func GetFileSize(file string) (int64, error) {
|
||||
fileInfo, err := os.Stat(file)
|
||||
if err != nil {
|
||||
|
|
@ -133,57 +36,6 @@ func GetFileSize(file string) (int64, error) {
|
|||
return size, nil
|
||||
}
|
||||
|
||||
func RemoveAccents(s string) string {
|
||||
t := transform.Chain(norm.NFD, runes.Remove(runes.In(unicode.Mn)), norm.NFC)
|
||||
output, _, e := transform.String(t, s)
|
||||
if e != nil {
|
||||
panic(e)
|
||||
}
|
||||
|
||||
return output
|
||||
}
|
||||
|
||||
func RemoveInvalidChars(input string, invalidChars []byte) string {
|
||||
filter := func(r rune) rune {
|
||||
for _, c := range invalidChars {
|
||||
if byte(r) == c {
|
||||
return -1 /* remove the char */
|
||||
}
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
return strings.Map(filter, input)
|
||||
}
|
||||
|
||||
func GetLocalIP() string {
|
||||
addrs, err := net.InterfaceAddrs()
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
for _, address := range addrs {
|
||||
if ipnet, ok := address.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {
|
||||
if ipnet.IP.To4() != nil {
|
||||
return ipnet.IP.String()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
/* catches interrupt signal (ctrl+c) */
|
||||
func SetupCloseHandler(tempDir, zipFile string) {
|
||||
c := make(chan os.Signal, 1)
|
||||
signal.Notify(c, os.Interrupt)
|
||||
go func() {
|
||||
<-c
|
||||
DeleteFile(tempDir)
|
||||
DeleteFile(zipFile)
|
||||
os.Exit(0)
|
||||
}()
|
||||
}
|
||||
|
||||
func DeleteFile(filePath string) {
|
||||
if _, err := os.Stat(filePath); err == nil {
|
||||
if err := os.RemoveAll(filePath); err != nil {
|
||||
|
|
@ -192,28 +44,6 @@ func DeleteFile(filePath string) {
|
|||
}
|
||||
}
|
||||
|
||||
/* used for the last validation in the Match function */
|
||||
func ExtractFirstWord(value string) string {
|
||||
for i := range value {
|
||||
if value[i] == ' ' {
|
||||
return value[0:i]
|
||||
}
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
/*
|
||||
i don't know why, but there are artists who,
|
||||
due to their name, they add a hyphen
|
||||
between some words of their names
|
||||
on one platform and not on the other
|
||||
*/
|
||||
func CleanAndNormalize(s string) string {
|
||||
cleaned := strings.ReplaceAll(s, "-", "")
|
||||
cleaned = strings.ReplaceAll(cleaned, " ", "")
|
||||
return cleaned
|
||||
}
|
||||
|
||||
// Convert M4A file from stereo to mono
|
||||
func ConvertM4aToMono(inputFile, outputFile string) ([]byte, error) {
|
||||
cmd := exec.Command("ffprobe", "-v", "error", "-show_entries", "stream=channels", "-of", "default=noprint_wrappers=1:nokey=1", inputFile)
|
||||
|
|
@ -229,11 +59,20 @@ func ConvertM4aToMono(inputFile, outputFile string) ([]byte, error) {
|
|||
|
||||
channels := strings.TrimSpace(string(output))
|
||||
if channels != "1" {
|
||||
// Convert to mono
|
||||
cmd = exec.Command("ffmpeg", "-i", inputFile, "-af", "pan=mono|c0=c0", outputFile)
|
||||
if err := cmd.Run(); err != nil {
|
||||
return nil, fmt.Errorf("error running ffmpeg: %v", err)
|
||||
}
|
||||
|
||||
// Resample to 8192 Hz
|
||||
// resampledFile := strings.TrimSuffix(outputFile, filepath.Ext(outputFile)) + "_resampled.m4a"
|
||||
// cmd = exec.Command("ffmpeg", "-i", outputFile, "-ar", "8192", resampledFile)
|
||||
// output, err = cmd.CombinedOutput()
|
||||
// if err := cmd.Run(); err != nil {
|
||||
// return nil, fmt.Errorf("error resampling: %v, %v", err, string(output))
|
||||
// }
|
||||
|
||||
audioBytes, err = ioutil.ReadFile(outputFile)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error reading input file: %v", err)
|
||||
|
|
|
|||
|
|
@ -5,6 +5,15 @@ import (
|
|||
"fmt"
|
||||
"log"
|
||||
|
||||
"errors"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/buger/jsonparser"
|
||||
"google.golang.org/api/option"
|
||||
"google.golang.org/api/youtube/v3"
|
||||
)
|
||||
|
|
@ -37,3 +46,169 @@ func VideoID(spTrack Track) (string, error) {
|
|||
// TODO: Handle when the query returns no songs (highly unlikely since the query is coming from spotify though)
|
||||
return "", nil
|
||||
}
|
||||
|
||||
var httpClient = &http.Client{}
|
||||
var durationMatchThreshold = 5
|
||||
|
||||
type SearchResult struct {
|
||||
Title, Uploader, URL, Duration, ID string
|
||||
Live bool
|
||||
SourceName string
|
||||
Extra []string
|
||||
}
|
||||
|
||||
func convertStringDurationToSeconds(durationStr string) int {
|
||||
splitEntities := strings.Split(durationStr, ":")
|
||||
if len(splitEntities) == 1 {
|
||||
seconds, _ := strconv.Atoi(splitEntities[0])
|
||||
return seconds
|
||||
} else if len(splitEntities) == 2 {
|
||||
seconds, _ := strconv.Atoi(splitEntities[1])
|
||||
minutes, _ := strconv.Atoi(splitEntities[0])
|
||||
return (minutes * 60) + seconds
|
||||
} else if len(splitEntities) == 3 {
|
||||
seconds, _ := strconv.Atoi(splitEntities[2])
|
||||
minutes, _ := strconv.Atoi(splitEntities[1])
|
||||
hours, _ := strconv.Atoi(splitEntities[0])
|
||||
return ((hours * 60) * 60) + (minutes * 60) + seconds
|
||||
} else {
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
||||
// GetYoutubeId takes the query as string and returns the search results video ID's
|
||||
func GetYoutubeId(spTrack Track) (string, error) {
|
||||
artists := strings.Join(spTrack.Artists, ", ")
|
||||
songDurationInSeconds := spTrack.Duration * 60
|
||||
searchQuery := fmt.Sprintf("'%s' %s %s", spTrack.Title, artists, spTrack.Album)
|
||||
|
||||
searchResults, err := ytSearch(searchQuery, 10)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if len(searchResults) == 0 {
|
||||
errorMessage := fmt.Sprintf("no songs found for %s", searchQuery)
|
||||
return "", errors.New(errorMessage)
|
||||
}
|
||||
// Try for the closest match timestamp wise
|
||||
for _, result := range searchResults {
|
||||
allowedDurationRangeStart := songDurationInSeconds - durationMatchThreshold
|
||||
allowedDurationRangeEnd := songDurationInSeconds + durationMatchThreshold
|
||||
resultSongDuration := convertStringDurationToSeconds(result.Duration)
|
||||
if resultSongDuration >= allowedDurationRangeStart && resultSongDuration <= allowedDurationRangeEnd {
|
||||
return result.ID, nil
|
||||
}
|
||||
}
|
||||
// Else return the first result if nothing is found
|
||||
return searchResults[0].ID, nil
|
||||
}
|
||||
|
||||
func getContent(data []byte, index int) []byte {
|
||||
id := fmt.Sprintf("[%d]", index)
|
||||
contents, _, _, _ := jsonparser.Get(data, "contents", "twoColumnSearchResultsRenderer", "primaryContents", "sectionListRenderer", "contents", id, "itemSectionRenderer", "contents")
|
||||
return contents
|
||||
}
|
||||
|
||||
func ytSearch(searchTerm string, limit int) (results []*SearchResult, err error) {
|
||||
ytSearchUrl := fmt.Sprintf(
|
||||
"https://www.youtube.com/results?search_query=%s", url.QueryEscape(searchTerm),
|
||||
)
|
||||
|
||||
req, err := http.NewRequest("GET", ytSearchUrl, nil)
|
||||
if err != nil {
|
||||
return nil, errors.New("cannot get youtube page")
|
||||
}
|
||||
req.Header.Add("Accept-Language", "en")
|
||||
res, err := httpClient.Do(req)
|
||||
if err != nil {
|
||||
return nil, errors.New("cannot get youtube page")
|
||||
}
|
||||
|
||||
defer func(Body io.ReadCloser) {
|
||||
_ = Body.Close()
|
||||
}(res.Body)
|
||||
|
||||
if res.StatusCode != 200 {
|
||||
return nil, errors.New("failed to make a request to youtube")
|
||||
}
|
||||
|
||||
buffer, err := ioutil.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
return nil, errors.New("cannot read response from youtube")
|
||||
}
|
||||
|
||||
body := string(buffer)
|
||||
splitScript := strings.Split(body, `window["ytInitialData"] = `)
|
||||
if len(splitScript) != 2 {
|
||||
splitScript = strings.Split(body, `var ytInitialData = `)
|
||||
}
|
||||
|
||||
if len(splitScript) != 2 {
|
||||
return nil, errors.New("invalid response from youtube")
|
||||
}
|
||||
splitScript = strings.Split(splitScript[1], `window["ytInitialPlayerResponse"] = null;`)
|
||||
jsonData := []byte(splitScript[0])
|
||||
|
||||
index := 0
|
||||
var contents []byte
|
||||
|
||||
for {
|
||||
contents = getContent(jsonData, index)
|
||||
_, _, _, err = jsonparser.Get(contents, "[0]", "carouselAdRenderer")
|
||||
|
||||
if err == nil {
|
||||
index++
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
_, err = jsonparser.ArrayEach(contents, func(value []byte, t jsonparser.ValueType, i int, err error) {
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if limit > 0 && len(results) >= limit {
|
||||
return
|
||||
}
|
||||
|
||||
id, err := jsonparser.GetString(value, "videoRenderer", "videoId")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
title, err := jsonparser.GetString(value, "videoRenderer", "title", "runs", "[0]", "text")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
uploader, err := jsonparser.GetString(value, "videoRenderer", "ownerText", "runs", "[0]", "text")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
live := false
|
||||
duration, err := jsonparser.GetString(value, "videoRenderer", "lengthText", "simpleText")
|
||||
|
||||
if err != nil {
|
||||
duration = ""
|
||||
live = true
|
||||
}
|
||||
|
||||
results = append(results, &SearchResult{
|
||||
Title: title,
|
||||
Uploader: uploader,
|
||||
Duration: duration,
|
||||
ID: id,
|
||||
URL: fmt.Sprintf("https://youtube.com/watch?v=%s", id),
|
||||
Live: live,
|
||||
SourceName: "youtube",
|
||||
})
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return results, err
|
||||
}
|
||||
|
||||
return results, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -68,7 +68,7 @@ func (db *DbClient) InsertChunkData(chunkfgp int64, chunkData interface{}) error
|
|||
err := chunksCollection.FindOne(context.Background(), filter).Decode(&result)
|
||||
if err == nil {
|
||||
// If the fingerprint already exists, append the chunkData to the existing list
|
||||
fmt.Println("DUPLICATE FINGERPRINT: ", chunkfgp)
|
||||
// fmt.Println("DUPLICATE FINGERPRINT: ", chunkfgp)
|
||||
update := bson.M{"$push": bson.M{"chunkData": chunkData}}
|
||||
_, err := chunksCollection.UpdateOne(context.Background(), filter, update)
|
||||
if err != nil {
|
||||
|
|
@ -88,28 +88,6 @@ func (db *DbClient) InsertChunkData(chunkfgp int64, chunkData interface{}) error
|
|||
return nil
|
||||
}
|
||||
|
||||
// func (db *DbClient) GetChunkData(chunkfgp int64) ([]interface{}, error) {
|
||||
// chunksCollection := db.client.Database("song-recognition").Collection("chunks")
|
||||
|
||||
// type chunkData struct {
|
||||
// ChunkData []interface{} `bson:"chunkData"`
|
||||
// }
|
||||
|
||||
// var result chunkData
|
||||
|
||||
// filter := bson.M{"fingerprint": chunkfgp}
|
||||
// err := chunksCollection.FindOne(context.Background(), filter).Decode(&result)
|
||||
|
||||
// if err != nil {
|
||||
// if err == mongo.ErrNoDocuments {
|
||||
// return nil, nil
|
||||
// }
|
||||
// return nil, fmt.Errorf("error retrieving chunk data: %w", err)
|
||||
// }
|
||||
|
||||
// return result.ChunkData, nil
|
||||
// }
|
||||
|
||||
type chunkData struct {
|
||||
SongName string `bson:"songName"`
|
||||
SongArtist string `bson:"songArtist"`
|
||||
|
|
@ -119,53 +97,6 @@ type chunkData struct {
|
|||
TimeStamp string `bson:"timeStamp"`
|
||||
}
|
||||
|
||||
// func (db *DbClient) GetChunkData(chunkfgp int64) ([]chunkData, error) {
|
||||
// chunksCollection := db.client.Database("song-recognition").Collection("chunks")
|
||||
|
||||
// var result []chunkData
|
||||
|
||||
// filter := bson.M{"fingerprint": chunkfgp}
|
||||
// err := chunksCollection.FindOne(context.Background(), filter).Decode(&result)
|
||||
|
||||
// if err != nil {
|
||||
// if err == mongo.ErrNoDocuments {
|
||||
// return nil, nil
|
||||
// }
|
||||
// return nil, fmt.Errorf("error retrieving chunk data: %w", err)
|
||||
// }
|
||||
|
||||
// return result, nil
|
||||
// }
|
||||
|
||||
// func (db *DbClient) GetChunkData(chunkfgp int64) ([]map[string]interface{}, error) {
|
||||
// chunksCollection := db.client.Database("song-recognition").Collection("chunks")
|
||||
|
||||
// // Change FindOne to Find to retrieve multiple documents
|
||||
// filter := bson.M{"fingerprint": chunkfgp}
|
||||
// cursor, err := chunksCollection.Find(context.Background(), filter)
|
||||
// if err != nil {
|
||||
// return nil, fmt.Errorf("error retrieving chunk data: %w", err)
|
||||
// }
|
||||
// defer cursor.Close(context.Background())
|
||||
|
||||
// var results []map[string]interface{}
|
||||
// for cursor.Next(context.Background()) {
|
||||
// var data map[string]interface{} // Assuming retrieved data is a map
|
||||
// if err := cursor.Decode(&data); err != nil {
|
||||
// return nil, fmt.Errorf("error decoding chunk data: %w", err)
|
||||
// }
|
||||
|
||||
// // Append original map to the results slice
|
||||
// results = append(results, data)
|
||||
// }
|
||||
|
||||
// if err := cursor.Err(); err != nil {
|
||||
// return nil, fmt.Errorf("error iterating through cursor: %w", err)
|
||||
// }
|
||||
|
||||
// return results, nil
|
||||
// }
|
||||
|
||||
func (db *DbClient) GetChunkData(chunkfgp int64) ([]primitive.M, error) {
|
||||
chunksCollection := db.client.Database("song-recognition").Collection("chunks")
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue