Compare commits
2 Commits
test-1
...
submit-web
| Author | SHA1 | Date | |
|---|---|---|---|
|
b6013ba1e3
|
|||
|
3457fde522
|
@@ -1,6 +1,39 @@
|
|||||||
# Getting Started with Create React App
|
# msw-open-music web font-end
|
||||||
|
|
||||||
This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
|
This msw-open-music project was bootstrapped with `Create React App`
|
||||||
|
|
||||||
|
## Group 9 information
|
||||||
|
|
||||||
|
| Name | Name (EN) | No |
|
||||||
|
| ------ | ------------- | ---------- |
|
||||||
|
| 陈永源 | CHEN Yongyuan | 1930006025 |
|
||||||
|
| 鲁雷 | Lu Lei | 2030026101 |
|
||||||
|
| 张滨玮 | Zhang Binwei | 2030026197 |
|
||||||
|
| 丁俊超 | Ding Junchao | 2030026258 |
|
||||||
|
| 邱星越 | Qiu Xingyue | 2030026119 |
|
||||||
|
| 李真晔 | Li Zhenye | 2030006104 |
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## URL References
|
||||||
|
|
||||||
|
- `/#/` Default home page. Generate random files.
|
||||||
|
- `/#/search-files` Search files
|
||||||
|
- `/#/search-folders` Search folders
|
||||||
|
- `/#/folder/:id` Show files in the folder
|
||||||
|
- `/#/file/:id` Show file's information
|
||||||
|
- `/#/file/:id/share` Share a specific file
|
||||||
|
- `/#/file/:id/review` Review a file
|
||||||
|
- `/#/manage` Manage system setting and status
|
||||||
|
- `/#/login` Login
|
||||||
|
- `/#/register/` Register
|
||||||
|
- `/#/profile/:id` Profile of a user
|
||||||
|
|
||||||
|
## HOW TO DEPLOY?
|
||||||
|
|
||||||
|
Welcome to visit the demo <https://demo.uicbbs.com>
|
||||||
|
|
||||||
|
Put the files under `build` folder to your HTTP server (Apache, nginx, caddy, etc.) root folder.
|
||||||
|
|
||||||
## Available Scripts
|
## Available Scripts
|
||||||
|
|
||||||
@@ -14,11 +47,6 @@ Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
|
|||||||
The page will reload if you make edits.\
|
The page will reload if you make edits.\
|
||||||
You will also see any lint errors in the console.
|
You will 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`
|
### `npm run build`
|
||||||
|
|
||||||
Builds the app for production to the `build` folder.\
|
Builds the app for production to the `build` folder.\
|
||||||
@@ -28,43 +56,3 @@ The build is minified and the filenames include the hashes.\
|
|||||||
Your app is ready to be deployed!
|
Your app is ready to be deployed!
|
||||||
|
|
||||||
See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
|
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)
|
|
||||||
|
|||||||
@@ -15,10 +15,18 @@ body {
|
|||||||
box-shadow: 0 0 8px #393939;
|
box-shadow: 0 0 8px #393939;
|
||||||
border-radius: 6px 6px 0 0;
|
border-radius: 6px 6px 0 0;
|
||||||
}
|
}
|
||||||
|
.avatar {
|
||||||
|
border-radius: 50%;
|
||||||
|
background-color: lightpink;
|
||||||
|
padding: 0.39rem;
|
||||||
|
}
|
||||||
.title {
|
.title {
|
||||||
margin-left: 1em;
|
margin-left: 1em;
|
||||||
|
margin-right: 1em;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
vertical-align: middle;
|
||||||
|
justify-content: space-between;
|
||||||
}
|
}
|
||||||
.title-text {
|
.title-text {
|
||||||
margin-left: 1em;
|
margin-left: 1em;
|
||||||
|
|||||||
@@ -1,9 +1,4 @@
|
|||||||
import {
|
import { HashRouter as Router, Routes, Route, NavLink } from "react-router-dom";
|
||||||
HashRouter as Router,
|
|
||||||
Routes,
|
|
||||||
Route,
|
|
||||||
NavLink,
|
|
||||||
} from "react-router-dom";
|
|
||||||
import "./App.css";
|
import "./App.css";
|
||||||
|
|
||||||
import GetRandomFiles from "./component/GetRandomFiles";
|
import GetRandomFiles from "./component/GetRandomFiles";
|
||||||
@@ -12,10 +7,18 @@ import SearchFolders from "./component/SearchFolders";
|
|||||||
import Manage from "./component/Manage";
|
import Manage from "./component/Manage";
|
||||||
import Share from "./component/Share";
|
import Share from "./component/Share";
|
||||||
import AudioPlayer from "./component/AudioPlayer";
|
import AudioPlayer from "./component/AudioPlayer";
|
||||||
|
import FilesInFolder from "./component/FilesInFolder";
|
||||||
|
import FileInfo from "./component/FileInfo";
|
||||||
|
import Review from "./component/Review";
|
||||||
|
import Profile from "./component/Profile";
|
||||||
|
import User from "./component/User";
|
||||||
|
import Login from "./component/Login";
|
||||||
|
import Register from "./component/Register";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
const [playingFile, setPlayingFile] = useState({});
|
const [playingFile, setPlayingFile] = useState({});
|
||||||
|
const [user, setUser] = useState(null);
|
||||||
return (
|
return (
|
||||||
<div className="base">
|
<div className="base">
|
||||||
<Router>
|
<Router>
|
||||||
@@ -23,6 +26,7 @@ function App() {
|
|||||||
<h3 className="title">
|
<h3 className="title">
|
||||||
<img src="favicon.png" alt="logo" className="logo" />
|
<img src="favicon.png" alt="logo" className="logo" />
|
||||||
<span className="title-text">MSW Open Music Project</span>
|
<span className="title-text">MSW Open Music Project</span>
|
||||||
|
<User user={user} setUser={setUser} />
|
||||||
</h3>
|
</h3>
|
||||||
<nav className="nav">
|
<nav className="nav">
|
||||||
<NavLink to="/" className="nav-link">
|
<NavLink to="/" className="nav-link">
|
||||||
@@ -55,14 +59,22 @@ function App() {
|
|||||||
element={<SearchFolders setPlayingFile={setPlayingFile} />}
|
element={<SearchFolders setPlayingFile={setPlayingFile} />}
|
||||||
/>
|
/>
|
||||||
<Route
|
<Route
|
||||||
path="/search-folders/:id"
|
path="/folder/:id"
|
||||||
element={<SearchFolders setPlayingFile={setPlayingFile} />}
|
element={<FilesInFolder setPlayingFile={setPlayingFile} />}
|
||||||
/>
|
/>
|
||||||
<Route path="/manage" element={<Manage />} />
|
<Route path="/manage" element={<Manage />} />
|
||||||
<Route
|
<Route
|
||||||
path="/share/:id"
|
path="/file/:id/share"
|
||||||
element={<Share setPlayingFile={setPlayingFile} />}
|
element={<Share setPlayingFile={setPlayingFile} />}
|
||||||
/>
|
/>
|
||||||
|
<Route path="/file/:id/review" element={<Review />} />
|
||||||
|
<Route
|
||||||
|
path="/profile/:id"
|
||||||
|
element={<Profile user={user} setUser={setUser} />}
|
||||||
|
/>
|
||||||
|
<Route path="/login" element={<Login setUser={setUser} />} />
|
||||||
|
<Route path="/register" element={<Register setUser={setUser} />} />
|
||||||
|
<Route path="/file/:id" element={<FileInfo />} />
|
||||||
</Routes>
|
</Routes>
|
||||||
</main>
|
</main>
|
||||||
<footer>
|
<footer>
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
|
import getFfmpegConfigListRespondExample from "../example-respond/get_ffmpeg_config_list.json"
|
||||||
|
|
||||||
function FfmpegConfig(props) {
|
function FfmpegConfig(props) {
|
||||||
// props.setSelectedFfmpegConfig
|
// props.setSelectedFfmpegConfig
|
||||||
@@ -7,17 +8,8 @@ function FfmpegConfig(props) {
|
|||||||
const [ffmpegConfigList, setFfmpegConfigList] = useState([]);
|
const [ffmpegConfigList, setFfmpegConfigList] = useState([]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetch("/api/v1/get_ffmpeg_config_list")
|
setFfmpegConfigList(getFfmpegConfigListRespondExample.ffmpeg_config_list);
|
||||||
.then((response) => response.json())
|
props.setSelectedFfmpegConfig(getFfmpegConfigListRespondExample.ffmpeg_config_list[0]);
|
||||||
.then((data) => {
|
|
||||||
setFfmpegConfigList(data.ffmpeg_config_list);
|
|
||||||
if (data.ffmpeg_config_list.length > 0) {
|
|
||||||
props.setSelectedFfmpegConfig(data.ffmpeg_config_list[0]);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
alert("get_ffmpeg_config_list error: " + error);
|
|
||||||
});
|
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -15,14 +15,11 @@ function FileDialog(props) {
|
|||||||
<dialog open={props.showStatus}>
|
<dialog open={props.showStatus}>
|
||||||
<p>{props.file.filename}</p>
|
<p>{props.file.filename}</p>
|
||||||
<p>
|
<p>
|
||||||
Download 使用浏览器下载原文件
|
Download using browser
|
||||||
<br />
|
<br />
|
||||||
Play 调用网页播放器播放
|
Play on the web page
|
||||||
<br />
|
<br />
|
||||||
</p>
|
</p>
|
||||||
<a href={downloadURL} download>
|
|
||||||
<button>Download</button>
|
|
||||||
</a>
|
|
||||||
<button
|
<button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
props.setPlayingFile(props.file);
|
props.setPlayingFile(props.file);
|
||||||
@@ -33,11 +30,10 @@ function FileDialog(props) {
|
|||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
navigate(`/share/${props.file.id}`);
|
navigate(`/file/${props.file.id}`);
|
||||||
props.setShowStatus(false);
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Share
|
Info
|
||||||
</button>
|
</button>
|
||||||
<button onClick={() => props.setShowStatus(false)}>Close</button>
|
<button onClick={() => props.setShowStatus(false)}>Close</button>
|
||||||
</dialog>
|
</dialog>
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ function FileEntry(props) {
|
|||||||
</td>
|
</td>
|
||||||
<td
|
<td
|
||||||
className="clickable"
|
className="clickable"
|
||||||
onClick={() => navigate(`/search-folders/${props.file.folder_id}`)}
|
onClick={() => navigate(`/folder/${props.file.folder_id}`)}
|
||||||
>
|
>
|
||||||
{props.file.foldername}
|
{props.file.foldername}
|
||||||
</td>
|
</td>
|
||||||
|
|||||||
69
web/src/component/FileInfo.js
Normal file
69
web/src/component/FileInfo.js
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
import { useParams, Link, useNavigate } from "react-router-dom";
|
||||||
|
|
||||||
|
function FileInfo() {
|
||||||
|
let params = useParams();
|
||||||
|
let navigate = useNavigate();
|
||||||
|
return (
|
||||||
|
<div className="page">
|
||||||
|
<h3>File Information</h3>
|
||||||
|
<span>
|
||||||
|
<button>Download</button>
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
navigate("/file/" + params.id + '/share');
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Share
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
navigate("/file/" + params.id + '/review');
|
||||||
|
}}
|
||||||
|
>Review</button>
|
||||||
|
</span>
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Name</th>
|
||||||
|
<th>Value</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>File Name</td>
|
||||||
|
<td>{params.id}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>File Size</td>
|
||||||
|
<td>123456</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>File Type</td>
|
||||||
|
<td>media/aac</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Last Modified</td>
|
||||||
|
<td>2020-01-01</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Import by</td>
|
||||||
|
<td>
|
||||||
|
<Link to="/profile/3">@admin</Link>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Import Date</td>
|
||||||
|
<td>2020-01-01</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Location</td>
|
||||||
|
<td>/data/media/aac</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<button>Update</button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default FileInfo;
|
||||||
15
web/src/component/FilesInFolder.js
Normal file
15
web/src/component/FilesInFolder.js
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import FilesTable from "./FilesTable";
|
||||||
|
import searchFilesRespondExample from "../example-respond/search_files.json"
|
||||||
|
import {useParams} from "react-router";
|
||||||
|
|
||||||
|
function FilesInFolder(props) {
|
||||||
|
let params = useParams();
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<h3>Files in folder id {params.id}</h3>
|
||||||
|
<FilesTable setPlayingFile={props.setPlayingFile} files={searchFilesRespondExample.files} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default FilesInFolder;
|
||||||
@@ -14,12 +14,12 @@ function FoldersTable(props) {
|
|||||||
{props.folders.map((folder) => (
|
{props.folders.map((folder) => (
|
||||||
<tr key={folder.id}>
|
<tr key={folder.id}>
|
||||||
<td
|
<td
|
||||||
onClick={() => navigate(`/search-folders/${folder.id}`)}
|
onClick={() => navigate(`/folder/${folder.id}`)}
|
||||||
className="clickable"
|
className="clickable"
|
||||||
>
|
>
|
||||||
{folder.foldername}
|
{folder.foldername}
|
||||||
</td>
|
</td>
|
||||||
<td onClick={() => navigate(`/search-folders/${folder.id}`)}>
|
<td onClick={() => navigate(`/folder/${folder.id}`)}>
|
||||||
<button>View</button>
|
<button>View</button>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|||||||
@@ -1,23 +1,13 @@
|
|||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import FilesTable from "./FilesTable";
|
import FilesTable from "./FilesTable";
|
||||||
|
import getRandomFilesRespondExample from "../example-respond/get_random_files.json"
|
||||||
|
|
||||||
function GetRandomFiles(props) {
|
function GetRandomFiles(props) {
|
||||||
const [files, setFiles] = useState([]);
|
const [files, setFiles] = useState([]);
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
|
||||||
function refresh(setFiles) {
|
function refresh(setFiles) {
|
||||||
setIsLoading(true);
|
setFiles(getRandomFilesRespondExample.files);
|
||||||
fetch("/api/v1/get_random_files")
|
|
||||||
.then((response) => response.json())
|
|
||||||
.then((data) => {
|
|
||||||
setFiles(data.files);
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
alert("get_random_files error: " + error);
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
setIsLoading(false);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
48
web/src/component/Login.js
Normal file
48
web/src/component/Login.js
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
import { useNavigate } from "react-router-dom";
|
||||||
|
import { useState } from "react";
|
||||||
|
|
||||||
|
function Login(props) {
|
||||||
|
let navigate = useNavigate();
|
||||||
|
let [username, setUsername] = useState("");
|
||||||
|
let [password, setPassword] = useState("");
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<h1>Login</h1>
|
||||||
|
<label htmlFor="username"></label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
id="username"
|
||||||
|
value={username}
|
||||||
|
onChange={(e) => setUsername(e.target.value)}
|
||||||
|
/>
|
||||||
|
<label htmlFor="password">Password</label>
|
||||||
|
<input
|
||||||
|
type="password"
|
||||||
|
id="password"
|
||||||
|
value={password}
|
||||||
|
onChange={(e) => setPassword(e.target.value)}
|
||||||
|
/>
|
||||||
|
<span>
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
if (!username || !password) {
|
||||||
|
alert("Please enter username and password");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
props.setUser({ id: 123, username: username, password: password });
|
||||||
|
navigate("/");
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Login
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
navigate("/register");
|
||||||
|
}}
|
||||||
|
>Register</button>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Login;
|
||||||
@@ -1,9 +1,104 @@
|
|||||||
|
import getFfmpegConfigListRespondExample from "../example-respond/get_ffmpeg_config_list.json";
|
||||||
|
|
||||||
function Manage() {
|
function Manage() {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div className="page">
|
||||||
<h2>Manage</h2>
|
<h2>Manage</h2>
|
||||||
</div>
|
<h3>Server status</h3>
|
||||||
)
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Name</th>
|
||||||
|
<th>Value</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td>Server status</td>
|
||||||
|
<td>
|
||||||
|
<span className="status-ok">OK</span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Server uptime</td>
|
||||||
|
<td>
|
||||||
|
<span>1 day, 23 hours, 59 minutes and 59 seconds</span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Server load</td>
|
||||||
|
<td>
|
||||||
|
<span>0.00 / 0.00 / 0.00</span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Server memory usage</td>
|
||||||
|
<td>
|
||||||
|
<span>0.00 MB</span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Server disk usage</td>
|
||||||
|
<td>
|
||||||
|
<span>0.00 MB</span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Server uptime</td>
|
||||||
|
<td>
|
||||||
|
<span>1 day, 23 hours, 59 minutes and 59 seconds</span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Server load</td>
|
||||||
|
<td>
|
||||||
|
<span>0.00 / 0.00 / 0.00</span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Server memory usage</td>
|
||||||
|
<td>
|
||||||
|
<span>0.00 MB</span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>Server disk usage</td>
|
||||||
|
<td>
|
||||||
|
<span>0.00 MB</span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<h3>Database opeartions</h3>
|
||||||
|
<ul>
|
||||||
|
<li>.mp3</li>
|
||||||
|
<li>.flac</li>
|
||||||
|
<li>.wav</li>
|
||||||
|
<li>.ogg</li>
|
||||||
|
<li>.aac</li>
|
||||||
|
<li>.m4a</li>
|
||||||
|
</ul>
|
||||||
|
<input type="text" placeholder=".mp3" />
|
||||||
|
<button>Add Pattern</button>
|
||||||
|
<input type="text" placeholder="/path/to/root" />
|
||||||
|
<button>Import</button>
|
||||||
|
<h3>Ffmpeg Settings</h3>
|
||||||
|
<ol>
|
||||||
|
{getFfmpegConfigListRespondExample.ffmpeg_config_list.map(
|
||||||
|
(item, index) => (
|
||||||
|
<li>
|
||||||
|
{item.name} {item.args}
|
||||||
|
</li>
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
</ol>
|
||||||
|
<span>
|
||||||
|
<input type="text" placeholder="name" />
|
||||||
|
<input type="text" placeholder="args" />
|
||||||
|
<button>Add</button>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Manage;
|
export default Manage;
|
||||||
|
|||||||
37
web/src/component/Profile.js
Normal file
37
web/src/component/Profile.js
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
import { useParams, useNavigate } from "react-router-dom";
|
||||||
|
import ReviewEntry from "./ReviewEntry";
|
||||||
|
import SearchFiles from "./SearchFiles";
|
||||||
|
import getRandomFilesRespondExample from "../example-respond/get_random_files.json";
|
||||||
|
|
||||||
|
function Profile(props) {
|
||||||
|
let params = useParams();
|
||||||
|
let navigate = useNavigate();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<h1>Profile of user {params.id}</h1>
|
||||||
|
{props.user && props.user.id === parseInt(params.id) && (
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
props.setUser(null);
|
||||||
|
navigate("/");
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Logout
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
<p>
|
||||||
|
Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam
|
||||||
|
doloremque, quidem quisquam, quisquam quisquam quisquam quisquam
|
||||||
|
dignissimos.
|
||||||
|
</p>
|
||||||
|
<h3>Reviews</h3>
|
||||||
|
<ReviewEntry />
|
||||||
|
<ReviewEntry />
|
||||||
|
<h3>Liked music</h3>
|
||||||
|
<SearchFiles folder={{}} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Profile;
|
||||||
51
web/src/component/Register.js
Normal file
51
web/src/component/Register.js
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
import { useNavigate } from "react-router-dom";
|
||||||
|
import { useState } from "react";
|
||||||
|
|
||||||
|
function Register(props) {
|
||||||
|
let navigate = useNavigate();
|
||||||
|
const [username, setUsername] = useState("");
|
||||||
|
const [password, setPassword] = useState("");
|
||||||
|
const [password2, setPassword2] = useState("");
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<h1>Register</h1>
|
||||||
|
<label htmlFor="username">Username:</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
id="username"
|
||||||
|
value={username}
|
||||||
|
onChange={(e) => setUsername(e.target.value)}
|
||||||
|
/>
|
||||||
|
<label htmlFor="password">Password:</label>
|
||||||
|
<input
|
||||||
|
type="password"
|
||||||
|
id="password"
|
||||||
|
value={password}
|
||||||
|
onChange={(e) => setPassword(e.target.value)}
|
||||||
|
/>
|
||||||
|
<label htmlFor="password2">Confirm Password:</label>
|
||||||
|
<input
|
||||||
|
type="password"
|
||||||
|
id="password2"
|
||||||
|
value={password2}
|
||||||
|
onChange={(e) => setPassword2(e.target.value)}
|
||||||
|
/>
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
if (!username || !password || !password2) {
|
||||||
|
alert("Please fill out all fields");
|
||||||
|
} else if (password !== password2) {
|
||||||
|
alert("Passwords do not match");
|
||||||
|
} else {
|
||||||
|
props.setUser({ id: 39, username: username, password: password });
|
||||||
|
navigate("/");
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
Register
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Register;
|
||||||
35
web/src/component/Review.js
Normal file
35
web/src/component/Review.js
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
import { useParams } from "react-router";
|
||||||
|
import { Link } from "react-router-dom";
|
||||||
|
import ReviewEntry from "./ReviewEntry";
|
||||||
|
|
||||||
|
function Review() {
|
||||||
|
let params = useParams();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="page">
|
||||||
|
<h3>Review on music ID {params.id}</h3>
|
||||||
|
<textarea
|
||||||
|
className="review-text"
|
||||||
|
placeholder="Write your review here"
|
||||||
|
></textarea>
|
||||||
|
<span>
|
||||||
|
<button>Submit</button>
|
||||||
|
<button>Add to fav</button>
|
||||||
|
</span>
|
||||||
|
<details open>
|
||||||
|
<summary>Liked by</summary>
|
||||||
|
<p>
|
||||||
|
<Link to="/profile/1">@User 1</Link>
|
||||||
|
<Link to="/profile/2">@User 2</Link>
|
||||||
|
<Link to="/profile/3">@User 3</Link>
|
||||||
|
<Link to="/profile/4">@User 4</Link>
|
||||||
|
</p>
|
||||||
|
</details>
|
||||||
|
<ReviewEntry />
|
||||||
|
<ReviewEntry />
|
||||||
|
<ReviewEntry />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Review;
|
||||||
18
web/src/component/ReviewEntry.js
Normal file
18
web/src/component/ReviewEntry.js
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import { Link} from "react-router-dom";
|
||||||
|
|
||||||
|
function ReviewEntry() {
|
||||||
|
return (
|
||||||
|
<p>
|
||||||
|
<h5>
|
||||||
|
<Link to="/profile/2">@rin</Link> comment music ID 39 at
|
||||||
|
2019-01-01 12:23:45
|
||||||
|
</h5>
|
||||||
|
Agree with <Link to="/profile/1">@hmsy</Link>. I also like how well the
|
||||||
|
musician plays the guitar. They are all very good. They really make the
|
||||||
|
song sound better. I like the way the bass plays and the way the guitar
|
||||||
|
sounds. I like the way the drums sound.
|
||||||
|
</p>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ReviewEntry;
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
import { useState, useEffect } from "react";
|
import { useState, useEffect } from "react";
|
||||||
import FilesTable from "./FilesTable";
|
import FilesTable from "./FilesTable";
|
||||||
|
import searchFilesRespondExample from "../example-respond/search_files.json"
|
||||||
|
|
||||||
function SearchFiles(props) {
|
function SearchFiles(props) {
|
||||||
const [files, setFiles] = useState([]);
|
const [files, setFiles] = useState([]);
|
||||||
@@ -9,38 +10,7 @@ function SearchFiles(props) {
|
|||||||
const limit = 10;
|
const limit = 10;
|
||||||
|
|
||||||
function searchFiles() {
|
function searchFiles() {
|
||||||
if (
|
setFiles(searchFilesRespondExample.files);
|
||||||
filename === "" &&
|
|
||||||
(props.folder === undefined || props.folder.id === undefined)
|
|
||||||
) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const folder = props.folder ? props.folder : {};
|
|
||||||
const url = folder.id
|
|
||||||
? "/api/v1/get_files_in_folder"
|
|
||||||
: "/api/v1/search_files";
|
|
||||||
setIsLoading(true);
|
|
||||||
fetch(url, {
|
|
||||||
method: "POST",
|
|
||||||
headers: { "Content-Type": "application/json" },
|
|
||||||
body: JSON.stringify({
|
|
||||||
filename: filename,
|
|
||||||
limit: limit,
|
|
||||||
offset: offset,
|
|
||||||
folder_id: folder.id,
|
|
||||||
}),
|
|
||||||
})
|
|
||||||
.then((response) => response.json())
|
|
||||||
.then((data) => {
|
|
||||||
const files = data.files ? data.files : [];
|
|
||||||
setFiles(files);
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
alert("get_files_in_folder error: " + error);
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
setIsLoading(false);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function nextPage() {
|
function nextPage() {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { useEffect, useState } from "react";
|
|||||||
import { useParams } from "react-router";
|
import { useParams } from "react-router";
|
||||||
import FoldersTable from "./FoldersTable";
|
import FoldersTable from "./FoldersTable";
|
||||||
import SearchFiles from "./SearchFiles";
|
import SearchFiles from "./SearchFiles";
|
||||||
|
import searchFoldersRespondExample from "../example-respond/search_folders.json";
|
||||||
|
|
||||||
function SearchFolders(props) {
|
function SearchFolders(props) {
|
||||||
const [foldername, setFoldername] = useState("");
|
const [foldername, setFoldername] = useState("");
|
||||||
@@ -12,35 +13,7 @@ function SearchFolders(props) {
|
|||||||
const limit = 10;
|
const limit = 10;
|
||||||
|
|
||||||
function searchFolder() {
|
function searchFolder() {
|
||||||
if (foldername === "") {
|
setFolders(searchFoldersRespondExample.folders);
|
||||||
return;
|
|
||||||
}
|
|
||||||
setIsLoading(true);
|
|
||||||
fetch("/api/v1/search_folders", {
|
|
||||||
method: "POST",
|
|
||||||
headers: { "Content-Type": "application/json" },
|
|
||||||
body: JSON.stringify({
|
|
||||||
foldername: foldername,
|
|
||||||
limit: limit,
|
|
||||||
offset: offset,
|
|
||||||
}),
|
|
||||||
})
|
|
||||||
.then((response) => response.json())
|
|
||||||
.then((data) => {
|
|
||||||
let folders;
|
|
||||||
if (data.folders) {
|
|
||||||
folders = data.folders;
|
|
||||||
} else {
|
|
||||||
folders = [];
|
|
||||||
}
|
|
||||||
setFolders(folders);
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
alert("search_folders error: " + error);
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
setIsLoading(false);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function nextPage() {
|
function nextPage() {
|
||||||
|
|||||||
@@ -1,25 +1,13 @@
|
|||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { useParams } from "react-router";
|
import { useParams } from "react-router";
|
||||||
import FilesTable from "./FilesTable";
|
import FilesTable from "./FilesTable";
|
||||||
|
import GetFileInfoRespondExample from "../example-respond/get_file_info.json";
|
||||||
|
|
||||||
function Share(props) {
|
function Share(props) {
|
||||||
let params = useParams();
|
let params = useParams();
|
||||||
const [file, setFile] = useState([]);
|
const [file, setFile] = useState([]);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetch("/api/v1/get_file_info", {
|
setFile([GetFileInfoRespondExample]);
|
||||||
method: "POST",
|
|
||||||
headers: { "Content-Type": "application/json" },
|
|
||||||
body: JSON.stringify({
|
|
||||||
id: parseInt(params.id),
|
|
||||||
}),
|
|
||||||
})
|
|
||||||
.then((response) => response.json())
|
|
||||||
.then((data) => {
|
|
||||||
setFile([data]);
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
alert("get_file_info error: " + error);
|
|
||||||
});
|
|
||||||
}, [params]);
|
}, [params]);
|
||||||
return (
|
return (
|
||||||
<div className="page">
|
<div className="page">
|
||||||
|
|||||||
23
web/src/component/User.js
Normal file
23
web/src/component/User.js
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import { useNavigate } from "react-router";
|
||||||
|
|
||||||
|
function User(props) {
|
||||||
|
// props.user
|
||||||
|
// props.setUser
|
||||||
|
let navigate = useNavigate();
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className="avatar"
|
||||||
|
onClick={() => {
|
||||||
|
if (props.user) {
|
||||||
|
navigate("/profile/" + props.user.id);
|
||||||
|
} else {
|
||||||
|
navigate("/login");
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{props.user ? props.user.username : "Login"}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default User;
|
||||||
12
web/src/example-respond/get_ffmpeg_config_list.json
Normal file
12
web/src/example-respond/get_ffmpeg_config_list.json
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"ffmpeg_config_list": [
|
||||||
|
{ "name": "OPUS 128k", "args": "-c:a libopus -ab 128k" },
|
||||||
|
{ "name": "OPUS 96k", "args": "-c:a libopus -ab 96k" },
|
||||||
|
{ "name": "OPUS 256k", "args": "-c:a libopus -ab 256k" },
|
||||||
|
{ "name": "OPUS 320k", "args": "-c:a libopus -ab 320k" },
|
||||||
|
{ "name": "OPUS 512k", "args": "-c:a libopus -ab 512k" },
|
||||||
|
{ "name": "AAC 128k", "args": "-c:a aac -ab 128k" },
|
||||||
|
{ "name": "AAC 256k", "args": "-c:a aac -ab 256k" },
|
||||||
|
{ "name": "全损音质 32k", "args": "-c:a libopus -ab 32k" }
|
||||||
|
]
|
||||||
|
}
|
||||||
7
web/src/example-respond/get_file_info.json
Normal file
7
web/src/example-respond/get_file_info.json
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"id": 9856,
|
||||||
|
"folder_id": 898,
|
||||||
|
"foldername": "[2021.05.12] TVアニメ「シャドーハウス」EDテーマ「ないない」/ReoNa [スペシャルエディション] [FLAC 96kHz/24bit]",
|
||||||
|
"filename": "03. 生きてるだけでえらいよ.flac",
|
||||||
|
"filesize": 122761032
|
||||||
|
}
|
||||||
74
web/src/example-respond/get_random_files.json
Normal file
74
web/src/example-respond/get_random_files.json
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
{
|
||||||
|
"files": [
|
||||||
|
{
|
||||||
|
"id": 9727,
|
||||||
|
"folder_id": 958,
|
||||||
|
"foldername": "garnet (narry) — emerald [GARN-0002] (flac)",
|
||||||
|
"filename": "06. narry — malachite.flac",
|
||||||
|
"filesize": 28228112
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 4785,
|
||||||
|
"folder_id": 457,
|
||||||
|
"foldername": "Winter (FLAC)",
|
||||||
|
"filename": "08 - 恋.flac",
|
||||||
|
"filesize": 33576086
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 13943,
|
||||||
|
"folder_id": 1368,
|
||||||
|
"foldername": "[mikudb] 融合YELLOWS",
|
||||||
|
"filename": "10. カレーライスのうた.mp3",
|
||||||
|
"filesize": 2524925
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 21743,
|
||||||
|
"folder_id": 2207,
|
||||||
|
"foldername": "Tsumanne\(^o^)/",
|
||||||
|
"filename": "08.スーパートルコ行進曲 - オワタ\(^o^)/.mp3",
|
||||||
|
"filesize": 7677985
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 35918,
|
||||||
|
"folder_id": 3758,
|
||||||
|
"foldername": "2008 - Higurashi 2 - Ano hi, Ano Basho, Subete ni ' Arigatou",
|
||||||
|
"filename": "06 - Thanks (Bashee Arenge Version).flac",
|
||||||
|
"filesize": 39545977
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 32394,
|
||||||
|
"folder_id": 3341,
|
||||||
|
"foldername": "[彩音 ~xi-on~] Eyes",
|
||||||
|
"filename": "08 - 少女さとり ~ 3rd Eye.mp3",
|
||||||
|
"filesize": 7538525
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 16934,
|
||||||
|
"folder_id": 1671,
|
||||||
|
"foldername": "Explorism",
|
||||||
|
"filename": "14.Please, My Producer (feat. SAK).mp3",
|
||||||
|
"filesize": 6099717
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 1381,
|
||||||
|
"folder_id": 131,
|
||||||
|
"foldername": "Garakuta Live (FLAC)",
|
||||||
|
"filename": "(09) [しけもく] 愛染エピローグ.flac",
|
||||||
|
"filesize": 25173829
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 18066,
|
||||||
|
"folder_id": 1791,
|
||||||
|
"foldername": "Jailbreak from the Sunday Morning!!",
|
||||||
|
"filename": "05. Good bye!! Melancory Sunday Morning!!.mp3",
|
||||||
|
"filesize": 10197778
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 41261,
|
||||||
|
"folder_id": 4305,
|
||||||
|
"foldername": "[KSLA-0124~6] Modification of Key Sounds Label [CD-FLAC]",
|
||||||
|
"filename": "3-01. Trigger (MUZIK SERVANT vs Freezer Remix).flac",
|
||||||
|
"filesize": 42772516
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
25
web/src/example-respond/search_files.json
Normal file
25
web/src/example-respond/search_files.json
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
"files": [
|
||||||
|
{
|
||||||
|
"id": 26555,
|
||||||
|
"folder_id": 2579,
|
||||||
|
"foldername": "[mikudb] SEB presents SUPER HATSUNE BEAT Vol. 1",
|
||||||
|
"filename": "02. White Letter (ゆうゆP Euro Arrange).mp3",
|
||||||
|
"filesize": 4252006
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 40891,
|
||||||
|
"folder_id": 4121,
|
||||||
|
"foldername": "初音ミクベスト ~memories~",
|
||||||
|
"filename": "10.White letter.mp3",
|
||||||
|
"filesize": 6723982
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 43289,
|
||||||
|
"folder_id": 4384,
|
||||||
|
"foldername": "Hatsune Miku Best ~memories~",
|
||||||
|
"filename": "10.White Letter.flac",
|
||||||
|
"filesize": 20817678
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
32
web/src/example-respond/search_folders.json
Normal file
32
web/src/example-respond/search_folders.json
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
{
|
||||||
|
"folders": [
|
||||||
|
{
|
||||||
|
"id": 2037,
|
||||||
|
"foldername": "P∴Rhythmatiq — 七色リミックス [OSLA-0005] (flac+scans)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 2130,
|
||||||
|
"foldername": " P∴Rhythmatiq — P∴Rhythmatiq act:09 [PRTQ-0017] (flac+scans)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 2176,
|
||||||
|
"foldername": "P∴Rhythmatiq — P∴Rhythmatiq act:02 [PRTQ-0002] (flac)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 2184,
|
||||||
|
"foldername": "P∴Rhythmatiq — P∴Rhythmatiq act:04 [PRTQ-0005] (flac)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 2190,
|
||||||
|
"foldername": "P∴Rhythmatiq — P∴Rhythmatiq Rock!! [PQPC-0001] (flac)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 2360,
|
||||||
|
"foldername": "P∴Rhythmatiq — P∴Rhythmatiq act:11 [PRTQ-0025] (flac)"
|
||||||
|
},
|
||||||
|
{ "id": 3443, "foldername": "P∴Rhythmatiq EXTRA" },
|
||||||
|
{ "id": 3444, "foldername": "P∴Rhythmatiq He:arts" },
|
||||||
|
{ "id": 3445, "foldername": "P∴Rhythmatiq Re act" },
|
||||||
|
{ "id": 3446, "foldername": "P∴Rhythmatiq Rock!!" }
|
||||||
|
]
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user