Compare commits
5 Commits
v1.1.0
...
submit-web
| Author | SHA1 | Date | |
|---|---|---|---|
|
b6013ba1e3
|
|||
|
3457fde522
|
|||
|
be2515231c
|
|||
|
2358335d4e
|
|||
|
1bef4d0272
|
47
README.md
47
README.md
@@ -8,6 +8,53 @@ A light weight personal music streaming platform.
|
|||||||
|
|
||||||
[toc]
|
[toc]
|
||||||
|
|
||||||
|
## TODO
|
||||||
|
|
||||||
|
- Restructure,为多人协作做好准备
|
||||||
|
|
||||||
|
### 前端部分更改
|
||||||
|
|
||||||
|
- 修复页面 CSS 溢出问题
|
||||||
|
- 显示操作执行世界
|
||||||
|
|
||||||
|
页面数量至少 10 个(目前 5 个),预计添加如下页面
|
||||||
|
|
||||||
|
- 文件详情页,可以修改单个文件的信息
|
||||||
|
- 文件评论页,可对文件进行评论
|
||||||
|
- 最新动态页,查看最近播放的曲目、最近的评论
|
||||||
|
- 登录/注册页,取代现有的 token 逻辑
|
||||||
|
- FfmpegConfigs 配置页面
|
||||||
|
- 意见反馈的查看页面
|
||||||
|
|
||||||
|
### 后端部分更改
|
||||||
|
|
||||||
|
- 返回操作执行时间
|
||||||
|
|
||||||
|
- 修复 Prepare 模式转码不完整但仍然被 tmpfs 记录为成功转码的问题
|
||||||
|
|
||||||
|
- FfmpegConfigs 由目前的字典格式改为列表格式
|
||||||
|
- 为 sqlite3 添加数据库单线程锁
|
||||||
|
- 添加外键约束
|
||||||
|
- Update 功能自动检查重复的项目并忽略,只添加新的项目
|
||||||
|
- Token 验证方法改为 暱称 + 密码 的方法,管理员使用 admin 保留关键字作为暱称。
|
||||||
|
|
||||||
|
需要 8 个 entities 和 6 个 relationship,目前有 3 个 entities,和 1 个 relationship
|
||||||
|
|
||||||
|
目前有
|
||||||
|
|
||||||
|
- files 文件表,数量 50,000
|
||||||
|
- folders 文件夹表,数量 3,000
|
||||||
|
- feedbacks 反馈留言表
|
||||||
|
|
||||||
|
计划添加
|
||||||
|
|
||||||
|
- users 用户表
|
||||||
|
- comments 管理表
|
||||||
|
- playbacks 播放记录表
|
||||||
|
- likes 点赞记录表
|
||||||
|
|
||||||
|
## 编译 & 构建
|
||||||
|
|
||||||
## How to build
|
## How to build
|
||||||
|
|
||||||
### Build the back-end server
|
### Build the back-end server
|
||||||
|
|||||||
36
docs/problem_description.md
Normal file
36
docs/problem_description.md
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
# DBMS Group Project Problem Description
|
||||||
|
|
||||||
|
- Group 1
|
||||||
|
|
||||||
|
The Internet infrastructure construction has made the network speed development faster. With the fast Internet, people are gradually migrating various data and services to the cloud. For example, NetEase Cloud Music, Spotify, and Apple Music, we call them streaming media platforms. The definition of streaming media platform is that users purchase the digital copyright of music and then play the music online on the platform.
|
||||||
|
|
||||||
|
Generally speaking, users cannot buy music that is not available on the platform. The user cannot download the digital file of the music (the user purchases the right to play instead of the right to copy). Users cannot upload their music to the platform.
|
||||||
|
|
||||||
|
However, in the era of digital copyright, there are still many advantages to getting original music files, such as no need to install a dedicated player; free copying to other devices (without violating copyright); no risk of music unavailable from the platform; no play records and privacy will be tracked by the platform.
|
||||||
|
|
||||||
|
Some people don't like streaming platforms. They like to collect music (download or buy CDs) and save it on their computers. But as more and more music is collected (over 70,000 songs and in total size of 800GB), it becomes very difficult to manage files. It is difficult for them to find where the songs they want to listen to are saved. Also, lossless music files are large and difficult to play online.
|
||||||
|
|
||||||
|
As long as there no such "Self-hosted music streaming platform" software available, we decided to develop a project based on database knowledge to help people who have collected a lot of music to enjoy their music simply.
|
||||||
|
|
||||||
|
We will handle various relevant types of data in our database. Including song name, album name, file size, update date, rating, comment, user information, etc. They are highly relevant, so using a relational database will be a good choice.
|
||||||
|
|
||||||
|
The features of the project we designed are as follows:
|
||||||
|
|
||||||
|
- Open. Independent front-end (GUI) and back-end (server program), using API to communicate.
|
||||||
|
- Easy to use. Minimize dependencies, allowing users to configure quickly and simply.
|
||||||
|
- Lightweight. The program is small in size and quick to install.
|
||||||
|
- High performance. Only do what should be done, no features that will lead to poor performance.
|
||||||
|
- Cross-platform. The project can run on computers, mobile phones, Linux, Windows, macOS, and X86 and ARM processor architectures.
|
||||||
|
- Extensibility. Access to cloud OSS (Object Storage Service), reverse proxy, or other external software.
|
||||||
|
|
||||||
|
Our project has the following functions:
|
||||||
|
|
||||||
|
- Index file. Index local files into the database.
|
||||||
|
- Search. Search for music based on name/album/tag/comment, sorted by rating or other columns.
|
||||||
|
- Play. Play music online, play music randomly and play music at a low bit rate on a bad network.
|
||||||
|
- User management. Users can register and log in.
|
||||||
|
- Comment. Users can give a like or comment on the music.
|
||||||
|
- Management. The administrator can upload music, update or delete the database.
|
||||||
|
- Share. Generate a link to share the music with others.
|
||||||
|
|
||||||
|
After research and discussion, in order to meet the above requirements, we decided to use the Golang programming language on the backend. SQLite as a database program. Vue as the front-end GUI interface.
|
||||||
@@ -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