2 Commits

Author SHA1 Message Date
b6013ba1e3 DBMS add web/README.md demo website and group info 2021-11-28 23:07:54 +08:00
3457fde522 DBMS Submit static webpage
Removed all fetch function
2021-11-28 18:56:33 +08:00
25 changed files with 628 additions and 170 deletions

View File

@@ -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
@@ -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.\
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`
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!
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 cant go back!**
If you arent 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 youre on your own.
You dont have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldnt feel obligated to use this feature. However we understand that this tool wouldnt be useful if you couldnt 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)

View File

@@ -15,10 +15,18 @@ body {
box-shadow: 0 0 8px #393939;
border-radius: 6px 6px 0 0;
}
.avatar {
border-radius: 50%;
background-color: lightpink;
padding: 0.39rem;
}
.title {
margin-left: 1em;
margin-right: 1em;
display: flex;
align-items: center;
vertical-align: middle;
justify-content: space-between;
}
.title-text {
margin-left: 1em;

View File

@@ -1,9 +1,4 @@
import {
HashRouter as Router,
Routes,
Route,
NavLink,
} from "react-router-dom";
import { HashRouter as Router, Routes, Route, NavLink } from "react-router-dom";
import "./App.css";
import GetRandomFiles from "./component/GetRandomFiles";
@@ -12,10 +7,18 @@ import SearchFolders from "./component/SearchFolders";
import Manage from "./component/Manage";
import Share from "./component/Share";
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";
function App() {
const [playingFile, setPlayingFile] = useState({});
const [user, setUser] = useState(null);
return (
<div className="base">
<Router>
@@ -23,6 +26,7 @@ function App() {
<h3 className="title">
<img src="favicon.png" alt="logo" className="logo" />
<span className="title-text">MSW Open Music Project</span>
<User user={user} setUser={setUser} />
</h3>
<nav className="nav">
<NavLink to="/" className="nav-link">
@@ -55,14 +59,22 @@ function App() {
element={<SearchFolders setPlayingFile={setPlayingFile} />}
/>
<Route
path="/search-folders/:id"
element={<SearchFolders setPlayingFile={setPlayingFile} />}
path="/folder/:id"
element={<FilesInFolder setPlayingFile={setPlayingFile} />}
/>
<Route path="/manage" element={<Manage />} />
<Route
path="/share/:id"
path="/file/:id/share"
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>
</main>
<footer>

View File

@@ -1,4 +1,5 @@
import { useEffect, useState } from "react";
import getFfmpegConfigListRespondExample from "../example-respond/get_ffmpeg_config_list.json"
function FfmpegConfig(props) {
// props.setSelectedFfmpegConfig
@@ -7,17 +8,8 @@ function FfmpegConfig(props) {
const [ffmpegConfigList, setFfmpegConfigList] = useState([]);
useEffect(() => {
fetch("/api/v1/get_ffmpeg_config_list")
.then((response) => response.json())
.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);
});
setFfmpegConfigList(getFfmpegConfigListRespondExample.ffmpeg_config_list);
props.setSelectedFfmpegConfig(getFfmpegConfigListRespondExample.ffmpeg_config_list[0]);
}, []);
return (

View File

@@ -15,14 +15,11 @@ function FileDialog(props) {
<dialog open={props.showStatus}>
<p>{props.file.filename}</p>
<p>
Download 使用浏览器下载原文件
Download using browser
<br />
Play 调用网页播放器播放
Play on the web page
<br />
</p>
<a href={downloadURL} download>
<button>Download</button>
</a>
<button
onClick={() => {
props.setPlayingFile(props.file);
@@ -33,11 +30,10 @@ function FileDialog(props) {
</button>
<button
onClick={() => {
navigate(`/share/${props.file.id}`);
props.setShowStatus(false);
navigate(`/file/${props.file.id}`);
}}
>
Share
Info
</button>
<button onClick={() => props.setShowStatus(false)}>Close</button>
</dialog>

View File

@@ -25,7 +25,7 @@ function FileEntry(props) {
</td>
<td
className="clickable"
onClick={() => navigate(`/search-folders/${props.file.folder_id}`)}
onClick={() => navigate(`/folder/${props.file.folder_id}`)}
>
{props.file.foldername}
</td>

View 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;

View 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;

View File

@@ -14,12 +14,12 @@ function FoldersTable(props) {
{props.folders.map((folder) => (
<tr key={folder.id}>
<td
onClick={() => navigate(`/search-folders/${folder.id}`)}
onClick={() => navigate(`/folder/${folder.id}`)}
className="clickable"
>
{folder.foldername}
</td>
<td onClick={() => navigate(`/search-folders/${folder.id}`)}>
<td onClick={() => navigate(`/folder/${folder.id}`)}>
<button>View</button>
</td>
</tr>

View File

@@ -1,23 +1,13 @@
import { useEffect, useState } from "react";
import FilesTable from "./FilesTable";
import getRandomFilesRespondExample from "../example-respond/get_random_files.json"
function GetRandomFiles(props) {
const [files, setFiles] = useState([]);
const [isLoading, setIsLoading] = useState(false);
function refresh(setFiles) {
setIsLoading(true);
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);
});
setFiles(getRandomFilesRespondExample.files);
}
useEffect(() => {

View 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;

View File

@@ -1,9 +1,104 @@
import getFfmpegConfigListRespondExample from "../example-respond/get_ffmpeg_config_list.json";
function Manage() {
return (
<div>
<div className="page">
<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;

View 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;

View 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;

View 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>&nbsp;
<Link to="/profile/2">@User 2</Link>&nbsp;
<Link to="/profile/3">@User 3</Link>&nbsp;
<Link to="/profile/4">@User 4</Link>&nbsp;
</p>
</details>
<ReviewEntry />
<ReviewEntry />
<ReviewEntry />
</div>
);
}
export default Review;

View 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;

View File

@@ -1,5 +1,6 @@
import { useState, useEffect } from "react";
import FilesTable from "./FilesTable";
import searchFilesRespondExample from "../example-respond/search_files.json"
function SearchFiles(props) {
const [files, setFiles] = useState([]);
@@ -9,38 +10,7 @@ function SearchFiles(props) {
const limit = 10;
function searchFiles() {
if (
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);
});
setFiles(searchFilesRespondExample.files);
}
function nextPage() {

View File

@@ -2,6 +2,7 @@ import { useEffect, useState } from "react";
import { useParams } from "react-router";
import FoldersTable from "./FoldersTable";
import SearchFiles from "./SearchFiles";
import searchFoldersRespondExample from "../example-respond/search_folders.json";
function SearchFolders(props) {
const [foldername, setFoldername] = useState("");
@@ -12,35 +13,7 @@ function SearchFolders(props) {
const limit = 10;
function searchFolder() {
if (foldername === "") {
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);
});
setFolders(searchFoldersRespondExample.folders);
}
function nextPage() {

View File

@@ -1,25 +1,13 @@
import { useEffect, useState } from "react";
import { useParams } from "react-router";
import FilesTable from "./FilesTable";
import GetFileInfoRespondExample from "../example-respond/get_file_info.json";
function Share(props) {
let params = useParams();
const [file, setFile] = useState([]);
useEffect(() => {
fetch("/api/v1/get_file_info", {
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);
});
setFile([GetFileInfoRespondExample]);
}, [params]);
return (
<div className="page">

23
web/src/component/User.js Normal file
View 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;

View 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" }
]
}

View File

@@ -0,0 +1,7 @@
{
"id": 9856,
"folder_id": 898,
"foldername": "[2021.05.12] TVアニメ「シャドーハウス」EDテーマ「ないない」ReoNa [スペシャルエディション] [FLAC 96kHz24bit]",
"filename": "03. 生きてるだけでえらいよ.flac",
"filesize": 122761032
}

View 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
}
]
}

View 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
}
]
}

View File

@@ -0,0 +1,32 @@
{
"folders": [
{
"id": 2037,
"foldername": "P∴Rhythmatiq — 七色リミックス [OSLA-0005] (flac+scans)"
},
{
"id": 2130,
"foldername": " P∴Rhythmatiq — P∴Rhythmatiq act09 [PRTQ-0017] (flac+scans)"
},
{
"id": 2176,
"foldername": "P∴Rhythmatiq — P∴Rhythmatiq act02 [PRTQ-0002] (flac)"
},
{
"id": 2184,
"foldername": "P∴Rhythmatiq — P∴Rhythmatiq act04 [PRTQ-0005] (flac)"
},
{
"id": 2190,
"foldername": "P∴Rhythmatiq — P∴Rhythmatiq Rock!! [PQPC-0001] (flac)"
},
{
"id": 2360,
"foldername": "P∴Rhythmatiq — P∴Rhythmatiq act11 [PRTQ-0025] (flac)"
},
{ "id": 3443, "foldername": "P∴Rhythmatiq EXTRA" },
{ "id": 3444, "foldername": "P∴Rhythmatiq Hearts" },
{ "id": 3445, "foldername": "P∴Rhythmatiq Re act" },
{ "id": 3446, "foldername": "P∴Rhythmatiq Rock!!" }
]
}