This commit is contained in:
2025-06-23 21:09:25 +09:00
commit 32cd5b9be8
50 changed files with 59220 additions and 0 deletions

1
ChartMelonPlayer/.env Normal file
View File

@@ -0,0 +1 @@
BROWSER=none

View File

@@ -0,0 +1,11 @@
{
"printWidth": 100,
"tabWidth": 2,
"singleQuote": true,
"trailingComma": "all",
"bracketSpacing": true,
"semi": true,
"useTabs": false,
"arrowParens": "avoid",
"endOfLine": "lf"
}

View File

@@ -0,0 +1 @@
module.exports = [require.resolve('./.webpack.config.js')]

View File

@@ -0,0 +1,5 @@
// define child rescript
module.exports = config => {
config.target = 'electron-renderer';
return config;
}

21
ChartMelonPlayer/LICENSE Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2019 Dong-Hyun Kim
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,2 @@
# ChartMelonPlayer
Melon Chart Player

15989
ChartMelonPlayer/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,71 @@
{
"name": "manga-viewer",
"version": "0.1.0",
"private": true,
"dependencies": {
"antd": "^3.25.1",
"electron-is-dev": "^1.1.0",
"emotion": "^10.0.23",
"frameless-titlebar": "^1.0.8",
"react": "^16.11.0",
"react-dom": "^16.11.0",
"react-scripts": "3.2.0"
},
"main": "public/electron.js",
"scripts": {
"start": "rescripts start",
"build": "rescripts build",
"test": "rescripts test",
"eject": "react-scripts eject",
"api": "cd src/lib && go run Apiserve.go",
"app": "concurrently \"yarn api\" \"yarn start\" \"wait-on http://localhost:3000 && electron .\""
},
"eslintConfig": {
"parser": "babel-eslint",
"extends": [
"airbnb"
],
"plugins": [
"react",
"jsx-a11y",
"import"
],
"rules": {
"linebreak-style": 0,
"import/no-extraneous-dependencies": 0,
"no-use-before-define": 0,
"jsx-a11y/no-static-element-interactions": 0,
"jsx-a11y/click-events-have-key-events": 0
}
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
},
"devDependencies": {
"@rescripts/cli": "^0.0.13",
"@rescripts/rescript-env": "^0.0.11",
"axios": "^0.19.0",
"babel-eslint": "^10.0.3",
"concurrently": "^5.0.0",
"electron": "^7.1.1",
"electron-builder": "^22.1.0",
"eslint": "6.1.0",
"eslint-config-airbnb": "18.0.1",
"eslint-plugin-import": "2.18.2",
"eslint-plugin-jsx-a11y": "6.2.3",
"eslint-plugin-react": "7.14.3",
"eslint-plugin-react-hooks": "1.7.0",
"prettier-eslint": "^9.0.0",
"prop-types": "^15.7.2",
"wait-on": "^3.3.0"
}
}

View File

@@ -0,0 +1,45 @@
const electron = require('electron');
const { app } = electron;
const { BrowserWindow } = electron;
const path = require('path');
const isDev = require('electron-is-dev');
let mainWindow;
function createWindow() {
mainWindow = new BrowserWindow({
width: 900,
height: 680,
frame: false,
titleBarStyle: 'hidden',
webPreferences: {
nodeIntegration: true,
webSecurity: false,
},
});
mainWindow.loadURL(
isDev ? 'http://localhost:3000' : `file://${path.join(__dirname, '../build/index.html')}`,
);
if (isDev) {
// Open the DevTools.
// BrowserWindow.addDevToolsExtension('<location to your react chrome extension>');
mainWindow.webContents.openDevTools();
}
mainWindow.on('closed', () => { mainWindow = null; });
}
app.on('ready', createWindow);
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit();
}
});
app.on('activate', () => {
if (mainWindow === null) {
createWindow();
}
});

View File

@@ -0,0 +1,9 @@
<!DOCTYPE html>
<head>
<meta charset="UTF-8">
<title>ChartMelon Player</title>
</head>
<body>
<div id="root"></div>
</body>
</html>

View File

@@ -0,0 +1,2 @@
# https://www.robotstxt.org/robotstxt.html
User-agent: *

View File

@@ -0,0 +1,6 @@
import React from 'react';
import Charting from './Components/Charting';
const App = () => <Charting />;
export default App;

View File

@@ -0,0 +1,78 @@
import React, { useState } from 'react';
import axios from 'axios';
import TitleBar from 'frameless-titlebar';
import { css } from 'emotion';
import { Button, Menu } from 'antd';
import SongItem from './SongItem';
const Charting = () => {
const [chartData, setChartData] = useState({});
const [loading, setLoading] = useState(false);
const [isloaded, setIsLoaded] = useState(false);
const loadData = async () => {
const { data } = await axios.get('http://localhost:3001/api');
setChartData(data);
setLoading(false);
setIsLoaded(true);
};
const syncLoading = () => {
setLoading(true);
loadData();
};
const regenLoading = () => {
// setLoading(true);
};
return (
<>
<TitleBar app="&nbsp;ChartMelon Player" />
<Menu mode="horizontal">
<Menu.Item disabled>
<Button type="primary" loading={loading} onClick={syncLoading}>
Sync
</Button>
&nbsp;
<Button type="danger" loading={false} onClick={regenLoading}>
DB Regen
</Button>
</Menu.Item>
</Menu>
<div className={style}>
{
isloaded
? <SongItem data={chartData} />
: <span>차트를 불러오시려면 Sync 버튼을 눌러주세요!</span>
}
</div>
</>
);
};
const style = css`
overflow-y: auto;
overflow-x: hidden;
position: absolute;
top: 76px;
bottom: 0;
left: 0;
right: 0;
margin-left: 10px;
::-webkit-scrollbar {
width: 10px;
height: 10px;
}
::-webkit-scrollbar-thumb {
background: rgba(90, 90, 90);
}
::-webkit-scrollbar-track {
background: rgba(0, 0, 0, 0.2);
}
`;
export default Charting;

View File

@@ -0,0 +1,43 @@
import React, { useState, useCallback, useEffect } from 'react';
import PropTypes from 'prop-types';
import { List, Avatar } from 'antd';
const SongItem = ({ data }) => {
const [initLoading, setInitLoading] = useState(true);
const [isLoading, setIsLoading] = useState(false);
const initLoadCallBack = useCallback(() => {
setInitLoading(false);
if (data) {
setIsLoading(true);
}
}, [data]);
useEffect(initLoadCallBack, [data, initLoadCallBack]);
return (isLoading
? (
<List
className="song-chart"
loading={initLoading}
itemLayout="horizontal"
dataSource={data}
renderItem={(src) => (
<List.Item>
<List.Item.Meta
avatar={<Avatar src={src.Img} />}
title={src.Name}
description={`${src.Artist.substring(0, src.Artist.length / 2)} [${src.Album}]`}
/>
</List.Item>
)}
/>
) : null
);
};
SongItem.propTypes = {
data: PropTypes.object.isRequired,
};
export default SongItem;

View File

@@ -0,0 +1,6 @@
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import 'antd/dist/antd.css';
ReactDOM.render(<App />, document.getElementById('root'));

View File

@@ -0,0 +1,114 @@
package main
import (
"encoding/json"
"fmt"
"log"
"net/http"
"strings"
"github.com/PuerkitoBio/goquery"
)
type Song struct {
Img string
Name string
Artist string
Album string
}
func parseMelon() []string {
// Request the HTML Page.
res, err := http.Get("https://www.melon.com/chart/index.htm")
if err != nil {
log.Fatal(err)
}
defer res.Body.Close()
if res.StatusCode != 200 {
log.Fatalf("Status Code Error: %d %s", res.StatusCode, res.Status)
}
// Load the HTML DOM
doc, err := goquery.NewDocumentFromReader(res.Body)
if err != nil {
log.Fatal(err)
}
var data []string
var img_data []string
// Find the Song items
doc.Find("div.ellipsis").Each(func(i int, s *goquery.Selection) {
// For Each item Found, Get the Band and Title
some := s.Find("a").Text()
data = append(data, some)
})
data = data[6:]
if strings.Contains(data[0], "재생") {
data = data[1:]
}
fmt.Println("len:", len(data))
// Find the Song img items
doc.Find("img").Each(func(i int, s *goquery.Selection) {
// For Each item Found, Get the Band and Title
value, isExist := s.Attr("src")
if isExist {
result := strings.Replace(value, "/melon/resize/120/quality/80/optimize", "", 1)
img_data = append(img_data, result)
}
})
img_data = img_data[26:]
img_data = img_data[:len(img_data) - 8]
if strings.Contains(img_data[0], "btn_next.png") {
img_data = img_data[1:]
}
fmt.Println("data: ", img_data)
fmt.Println("len:", len(img_data))
for i := 0; i < 100; i++ {
temp := append([]string{img_data[i]}, data[i + (3 * i):]...)
data = append(data[:i + (3 * i)], temp...)
}
return data
}
func defaultHandler(w http.ResponseWriter, r *http.Request) {
r.ParseForm()
fmt.Println("default : ", r.Form)
fmt.Println("path", r.URL.Path)
fmt.Println("param : ", r.Form["test_param"])
for k, v := range r.Form {
fmt.Println("key : ", k)
fmt.Println("val : ", strings.Join(v, ""))
}
var data = parseMelon()
songs := []Song{}
fmt.Println("len:", len(data))
// Not Clean Artist - Artist * 2
for i := 0; i < 400; i += 4 {
sng := Song{Img: data[i], Name: data[i + 1], Artist: data[i + 2], Album: data[i + 3]}
songs = append(songs, sng)
}
doc, _ := json.Marshal(songs)
fmt.Fprintf(w, string(doc))
}
func main() {
http.HandleFunc("/api", defaultHandler)
err := http.ListenAndServe(":3001", nil)
if err != nil {
log.Fatal("ListenAndServe : ", err)
} else {
fmt.Println("ListenAndServe Started! -> Port(3001)")
}
}

File diff suppressed because it is too large Load Diff

12648
ChartMelonPlayer/yarn.lock Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,69 @@
#include <iostream>
#include <string>
struct Node
{
std::string data;
int n;
Node* nextNode;
};
void justStrcpy(char *dest, const char *src)
{
// for (char *save = dest; *dest++ = *src++; );
// *dest++ ... ?
// *(dest = dest + 1)...
// is *dest++ equal *(dest++)?
for (int i = 0;; ++i)
{
if(!*dest) return;
*dest = *src;
dest++;
src++;
}
}
Node* useNode(std::string data)
{
Node* newNode = new Node();
newNode->data = data;
newNode->n = 0;
newNode->nextNode = NULL;
return newNode;
}
Node* insertNode(Node* head, std::string data)
{
Node* insNode = useNode(data);
insNode->nextNode = head->nextNode;
head->nextNode = insNode;
return insNode;
}
void printNode(Node* head)
{
for (Node* curr = head; curr; curr = curr->nextNode)
{
std::cout << curr->data << std::endl;
}
}
int main()
{
char ppap[] = "ppap";
char bbab[] = "bbab";
justStrcpy(ppap, bbab);
std::cout << ppap << std::endl;
Node* header = useNode("Header Node!");
Node* A = insertNode(header, "A");
Node* B = insertNode(A, "B");
Node* C = insertNode(B, "C");
Node* D = insertNode(header, "D");
printNode(header);
return 0;
}

View File

@@ -0,0 +1,36 @@
#include <iostream>
class Animal
{
private:
int food;
int weight;
public:
void setAnimal(int _food, int _weight)
{
food = _food;
weight = _weight;
}
void increaseFood(int inc)
{
food += inc;
weight += inc / 3;
}
void viewStat()
{
std::cout << "이 동물의 Food : " << food << std::endl;
std::cout << "이 동물의 Weight : " << weight << std::endl;
}
};
int main()
{
Animal animal;
animal.setAnimal(100, 50);
animal.increaseFood(30);
animal.viewStat();
return 0;
}

View File

@@ -0,0 +1,56 @@
#include <string>
#include <iostream>
class Test
{
std::string str;
public:
Test(std::string _str)
{
str = _str;
std::cout << "생성자 호출! " << str << std::endl;
}
~Test()
{
std::cout << "소멸자 호출! " << str << std::endl;
}
};
class Foo
{
int number;
public:
Foo(int inp) : number(inp) {}
int& accessNumber() { return number; }
int getNumber() { return number; }
void showNumber() { std::cout << number << std::endl; }
};
void simpleFunction()
{
Test B("B Class");
}
int main()
{
// Test A("A Class");
// simpleFunction();
Foo a(5);
a.showNumber();
int& c = a.accessNumber();
c = 4;
a.showNumber();
int d = a.accessNumber();
d = 3;
a.showNumber();
int f = a.getNumber();
f = 1;
a.showNumber();
}

View File

@@ -0,0 +1,31 @@
#include <iostream>
class A {
int x;
public:
A(int c) : x(c) {}
A(const A &a) {
x = a.x;
std::cout << "복사 생성" << std::endl;
}
};
class B {
A a;
public:
B(int c) : a(c) {}
B(const B &b) : a(b.a) {}
A get_A() {
A temp(a);
return temp;
}
};
int main() {
B b(10);
std::cout << "---------" << std::endl;
A a1 = b.get_A();
}

View File

@@ -0,0 +1,80 @@
#include <iostream>
class Date
{
private:
int year_, month_, day_;
int getMonthsMaxDay(int year, int month)
{
int months[12] = { 31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
if (month == 2)
{
if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0)
months[1] = 29;
else months[1] = 28;
}
return months[month - 1];
}
public:
Date();
Date(int year, int month, int day);
void addDay(int inc);
void addMonth(int inc);
void addYear(int inc);
void showDate();
};
Date::Date()
: year_(2004), month_(01), day_(01) {}
Date::Date(int year, int month, int day)
: year_(year), month_(month), day_(day) {}
void Date::addDay(int inc)
{
day_ += inc;
for (; day_ > getMonthsMaxDay(year_, month_);)
{
day_ -= getMonthsMaxDay(year_, month_);
addMonth(1);
}
}
void Date::addMonth(int inc)
{
month_ += inc;
for (; month_ > 12;)
{
month_ -= 12;
addYear(1);
}
}
void Date::addYear(int inc)
{
year_ += inc;
}
void Date::showDate()
{
if (month_ < 10 && day_ < 10)
std::cout << "Now Date() : " << year_ << "-0" << month_ << "-0" << day_ << std::endl;
else if (month_ < 10 && day_ > 9)
std::cout << "Now Date() : " << year_ << "-0" << month_ << "-" << day_ << std::endl;
else if (month_ > 9 && day_ < 10)
std::cout << "Now Date() : " << year_ << "-" << month_ << "-0" << day_ << std::endl;
else
std::cout << "Now Date() : " << year_ << "-" << month_ << "-" << day_ << std::endl;
}
int main()
{
Date date(2020, 10, 10);
date.showDate();
}

View File

@@ -0,0 +1,15 @@
#include <iostream>
namespace header1
{
int foo()
{
std::cout << "This is H1 - foo()" << std::endl;
return 0;
}
void bar()
{
std::cout << "This is H1 - bar()" << std::endl;
}
}

View File

@@ -0,0 +1,15 @@
#include <iostream>
namespace header2
{
int foo()
{
std::cout << "This is H2 - foo()" << std::endl;
return 0;
}
void bar()
{
std::cout << "This is H2 - bar()" << std::endl;
}
}

View File

@@ -0,0 +1,62 @@
#include <cstdio>
class legend
{
private:
/* data */
public:
legend();
~legend();
void q1();
void q2();
};
legend::legend()
{
printf("default create function call\n");
}
legend::~legend()
{
printf("default delete function call");
}
// 1번 문제
void legend::q1()
{
for (int i = 4; i >= 0; --i)
{
for (int j = 0; j < i; ++j)
{
printf(" ");
}
for (int k = 0; k < 9 - (i * 2); ++k)
{
printf("*");
}
printf("\n");
}
printf(" ||| \n");
}
// 2번 문제
void legend::q2()
{
double scoreAvg;
printf("이번 학기에 받고 싶은 전과목 평균 점수가 무엇인고?\n");
scanf("%lf", &scoreAvg);
printf("너의 이번 학기 전과목 평균 점수는 %.1lf점이 될 것이니라~\n", scoreAvg);
}
int main()
{
legend legeno;
legeno.q1();
legeno.q2();
}

View File

@@ -0,0 +1,11 @@
#include <iostream>
#include "header1.h"
#include "header2.h"
using namespace header1;
int main()
{
foo();
header2::foo();
}

View File

@@ -0,0 +1,4 @@
// .babelrc
{
"presets" : ["@babel/preset-env"]
}

View File

@@ -0,0 +1,21 @@
{
"name": "FloChartAPIServer",
"version": "1.0.0",
"description": "FloChart-API-Server",
"main": "index.js",
"license": "MIT",
"dependencies": {
"apollo-server": "^2.17.0",
"axios": "^0.20.0",
"graphql": "^15.3.0"
},
"scripts": {
"start": "nodemon --exec babel-node src/index.js"
},
"devDependencies": {
"@babel/core": "^7.11.6",
"@babel/node": "^7.10.5",
"@babel/preset-env": "^7.11.5",
"nodemon": "^2.0.4"
}
}

View File

@@ -0,0 +1,3 @@
const songlists = [];
export default songlists;

View File

@@ -0,0 +1,30 @@
import songlists from '../database/songlists';
const resolvers = {
Query: {
songs: () => songlists,
song: (_, { rank }) => {
return songlists.filter(song => song.rank === rank)[0];
}
},
Mutation: {
addSong: (_, { name, artist, album, img }) => {
if (songlists.find(song => song.name === name)) return null;
const newSong = {
id : songlists.length + 1,
rank: songlists.length + 1,
name: name,
artist: artist,
album: album,
img: img
};
songlists.push(newSong);
return newSong;
}
}
}
export default resolvers;

View File

@@ -0,0 +1,23 @@
import { gql } from 'apollo-server';
const typeDefs = gql`
type Song {
id : Int!
rank: Int!
name: String!
artist: String!
album: String!
img: String!
}
type Query {
songs: [Song!]!
song(id: Int!): Song
}
type Mutation {
addSong(name: String!, artist: String!, album: String!, img: String!): Song!
}
`;
export default typeDefs;

View File

@@ -0,0 +1,14 @@
import { ApolloServer } from 'apollo-server';
import resolvers from './graphql/resolvers';
import typeDefs from './graphql/typeDefs';
// ApolloServer는 스키마와 리졸버가 반드시 필요함
const server = new ApolloServer({
typeDefs,
resolvers
});
// listen 함수로 웹 서버 실행
server.listen().then(({ url }) => {
console.log(`🚀 Server ready at ${url}`);
});

3288
FloChartAPIServer/yarn.lock Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,11 @@
{
"printWidth": 100,
"tabWidth": 2,
"singleQuote": true,
"trailingComma": "all",
"bracketSpacing": true,
"semi": true,
"useTabs": false,
"arrowParens": "avoid",
"endOfLine": "lf"
}

View File

@@ -0,0 +1 @@
module.exports = [require.resolve('./.webpack.config.js')]

View File

@@ -0,0 +1,5 @@
// define child rescript
module.exports = config => {
config.target = 'electron-renderer';
return config;
}

21
FloChartPlayer/LICENSE Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2019 Dong-Hyun Kim
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

3
FloChartPlayer/README.md Normal file
View File

@@ -0,0 +1,3 @@
# FloChart Player
...TODO - Use YT API

View File

@@ -0,0 +1,73 @@
{
"name": "floChart-player",
"version": "0.1.0",
"private": true,
"dependencies": {
"@apollo/client": "^3.1.4",
"@apollo/react-hooks": "^4.0.0",
"antd": "^3.25.1",
"apollo-boost": "^0.4.9",
"electron-is-dev": "^1.1.0",
"emotion": "^10.0.23",
"frameless-titlebar": "^1.0.8",
"graphql": "^15.3.0",
"react": "^16.11.0",
"react-dom": "^16.11.0",
"react-scripts": "3.2.0"
},
"main": "public/electron.js",
"scripts": {
"start": "rescripts start",
"build": "rescripts build",
"test": "rescripts test",
"eject": "react-scripts eject",
"app": "concurrently \"yarn start\" \"wait-on http://localhost:3000 && electron .\""
},
"eslintConfig": {
"parser": "babel-eslint",
"extends": [
"airbnb"
],
"plugins": [
"react",
"jsx-a11y",
"import"
],
"rules": {
"linebreak-style": 0,
"import/no-extraneous-dependencies": 0,
"no-use-before-define": 0,
"jsx-a11y/no-static-element-interactions": 0,
"jsx-a11y/click-events-have-key-events": 0
}
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
},
"devDependencies": {
"@rescripts/cli": "^0.0.13",
"@rescripts/rescript-env": "^0.0.11",
"axios": "^0.19.0",
"concurrently": "^5.0.0",
"electron": "^7.1.1",
"electron-builder": "^22.1.0",
"eslint": "6.1.0",
"eslint-config-airbnb": "18.0.1",
"eslint-plugin-import": "2.18.2",
"eslint-plugin-jsx-a11y": "6.2.3",
"eslint-plugin-react": "7.14.3",
"eslint-plugin-react-hooks": "1.7.0",
"prettier-eslint": "^9.0.0",
"prop-types": "^15.7.2",
"wait-on": "^3.3.0"
}
}

View File

@@ -0,0 +1,45 @@
const electron = require('electron');
const { app } = electron;
const { BrowserWindow } = electron;
const path = require('path');
const isDev = require('electron-is-dev');
let mainWindow;
function createWindow() {
mainWindow = new BrowserWindow({
width: 900,
height: 680,
frame: false,
titleBarStyle: 'hidden',
webPreferences: {
nodeIntegration: true,
webSecurity: false,
},
});
mainWindow.loadURL(
isDev ? 'http://localhost:3000' : `file://${path.join(__dirname, '../build/index.html')}`,
);
if (isDev) {
// Open the DevTools.
// BrowserWindow.addDevToolsExtension('<location to your react chrome extension>');
mainWindow.webContents.openDevTools();
}
mainWindow.on('closed', () => { mainWindow = null; });
}
app.on('ready', createWindow);
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit();
}
});
app.on('activate', () => {
if (mainWindow === null) {
createWindow();
}
});

View File

@@ -0,0 +1,9 @@
<!DOCTYPE html>
<head>
<meta charset="UTF-8">
<title>FloChart Player</title>
</head>
<body>
<div id="root"></div>
</body>
</html>

View File

@@ -0,0 +1,2 @@
# https://www.robotstxt.org/robotstxt.html
User-agent: *

View File

@@ -0,0 +1,6 @@
import React from 'react';
import Charting from './Components/Charting';
const App = () => <Charting />;
export default App;

View File

@@ -0,0 +1,114 @@
import React, { useState } from 'react';
import axios from 'axios';
import TitleBar from 'frameless-titlebar';
import { css } from 'emotion';
import { Button, Menu } from 'antd';
import SongItem from './SongItem';
import { gql, useMutation } from '@apollo/client';
import { useQuery } from '@apollo/react-hooks';
const ADD_SONG = gql`
mutation AddSong($name: String!, $artist: String!, $album: String!, $img: String!) {
addSong(name: $name, artist: $artist, album: $album, img: $img) {
id
name
artist
album
img
}
}
`;
const GET_SONGLIST = gql`
{
songs {
rank
name
artist
album
img
}
}
`;
const Charting = () => {
const [addSong, { songdata }] = useMutation(ADD_SONG);
const [chartData, setChartData] = useState({});
const [loading, setLoading] = useState(false);
const [isloaded, setIsLoaded] = useState(false);
const { loading_, error, data } = useQuery(GET_SONGLIST);
const jsonLoading = async () => {
const { data } = await axios.get('https://www.music-flo.com/api/meta/v1/chart/track/1');
setChartData(data.data);
console.log("ok.");
};
const syncLoading = () => {
setLoading(true);
setLoading(false);
console.log(data?.songs);
setIsLoaded(true);
};
const regenLoading = async () => {
chartData.trackList.forEach(song => {
console.log(song.name, song.artistList[0].name, song.album.title, song.album.imgList[5].url);
addSong({ variables: { name: song.name, artist: song.artistList[0].name, album: song.album.title, img: song.album.imgList[5].url } });
});
};
return (
<>
<TitleBar app="&nbsp;FloChart Player" />
<Menu mode="horizontal">
<Menu.Item disabled>
<Button type="primary" loading={loading} onClick={syncLoading}>
Sync
</Button>
&nbsp;
<Button type="danger" loading={false} onClick={regenLoading}>
DB Regen
</Button>
&nbsp;
<Button type="primary" loading={false} onClick={jsonLoading}>
Get JSON
</Button>
</Menu.Item>
</Menu>
<div className={style}>
{
isloaded
? <SongItem data={data?.songs} />
: <span>차트를 불러오시려면 Sync 버튼을 눌러주세요!</span>
}
</div>
</>
);
};
const style = css`
overflow-y: auto;
overflow-x: hidden;
position: absolute;
top: 76px;
bottom: 0;
left: 0;
right: 0;
margin-left: 10px;
::-webkit-scrollbar {
width: 10px;
height: 10px;
}
::-webkit-scrollbar-thumb {
background: rgba(90, 90, 90);
}
::-webkit-scrollbar-track {
background: rgba(0, 0, 0, 0.2);
}
`;
export default Charting;

View File

@@ -0,0 +1,43 @@
import React, { useState, useCallback, useEffect } from 'react';
import PropTypes from 'prop-types';
import { List, Avatar } from 'antd';
const SongItem = ({ data }) => {
const [initLoading, setInitLoading] = useState(true);
const [isLoading, setIsLoading] = useState(false);
const initLoadCallBack = useCallback(() => {
setInitLoading(false);
if (data) {
setIsLoading(true);
}
}, [data]);
useEffect(initLoadCallBack, [data, initLoadCallBack]);
return (isLoading
? (
<List
className="song-chart"
loading={initLoading}
itemLayout="horizontal"
dataSource={data}
renderItem={(src) => (
<List.Item>
<List.Item.Meta
avatar={<Avatar src={src.img} />}
title={src.name}
description={`${src.name} - ${src.artist} [${src.album}]`}
/>
</List.Item>
)}
/>
) : null
);
};
SongItem.propTypes = {
data: PropTypes.object.isRequired,
};
export default SongItem;

View File

@@ -0,0 +1,7 @@
import ApolloClient from 'apollo-boost';
const client = new ApolloClient({
uri: 'http://localhost:4000/'
});
export default client;

View File

@@ -0,0 +1,13 @@
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import 'antd/dist/antd.css';
import { ApolloProvider } from '@apollo/react-hooks';
import client from './apollo';
ReactDOM.render(
<ApolloProvider client={client}>
<App />
</ApolloProvider>,
document.getElementById('root')
);

13213
FloChartPlayer/yarn.lock Normal file

File diff suppressed because it is too large Load Diff

128
arduino/p1.ino Normal file
View File

@@ -0,0 +1,128 @@
#include <Stepper.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <swRTC.h>
swRTC rtc; //클래스 개체 선언
LiquidCrystal_I2C lcd(0x27, 16, 2);
const int stepsPerRevolution = 64;
int flag = 0;
int btnA = 7;
int buz = 6;
Stepper myStepper(stepsPerRevolution, 11,9,10,8);
int cds = A0;
const int TriggerPin = 12, EchoPin = 13;
long Dist = 0;
void setup()
{
rtc.stopRTC(); //정지
rtc.setTime(9, 00, 00); //시, 분, 초
rtc.setDate(23, 11, 2019); //일, 월, 년
rtc.startRTC(); //시작
// LCD Init
lcd.init();
lcd.backlight();
Serial.begin(9600);
// Motor Init
myStepper.setSpeed(300);
pinMode(btnA, INPUT_PULLUP);
pinMode(buz,OUTPUT);
}
long Distance(long time)
{
// Calculates the Distance in mm
// ((time)*(Speed of sound))/ toward and backward of object) * 10
long DistanceCalc; // Calculation variable
DistanceCalc = ((time / 2.9) / 2); // Actual calculation in mm
//DistanceCalc = time / 74 / 2; // Actual calculation in inches
return DistanceCalc; // return calculated value
}
void banggule(int where)
{
if (where == 1)
{
// Motor Rotate -> Right
for(int i = 0; i < 8; i++)
{
myStepper.step(-stepsPerRevolution);
}
}
else if (where == -1)
{
// Motor Rotate -> Left
for(int i = 0; i < 8; i++)
{
myStepper.step(stepsPerRevolution);
}
}
}
long digitS()
{
digitalWrite(TriggerPin, LOW);
delayMicroseconds(2);
digitalWrite(TriggerPin, HIGH); // Trigger pin to HIGH
delayMicroseconds(10); // 10us high
digitalWrite(TriggerPin, LOW); // Trigger pin to HIGH
Dist = pulseIn(EchoPin, HIGH); // Waits for the echo pin to get high
// returns the Duration in microseconds
long Distance_mm = Distance(Dist); // Use function to calculate the distance
return Distance_mm;
}
void loop()
{
// Analog Read
int cdsVal = analogRead(cds);
// LCD Cursor Set + Light Level Print
lcd.setCursor(0, 0);
lcd.print(cdsVal);
if (flag == 1 && cdsVal > 500) {
lcd.print(" - Li Lv Low");
banggule(-1);
flag = 0;
}
else if(flag == 0 && cdsVal > 500)
{
lcd.print(" - Li Lv Low");
banggule(1);
flag = 1;
}
else lcd.print(" - Li Lv High");
// Second Line
lcd.setCursor(0, 1);
lcd.print(rtc.getHours());
lcd.print(rtc.getMinutes());
lcd.print(rtc.getSeconds());
lcd.print(rtc.getYear());
lcd.print(rtc.getMonth());
lcd.print(rtc.getDay());
if(digitalRead(btnA) == LOW)
{
rtc.setTime(21, 00, 00);
}
if(digitS() > 0 && digitS() < 200 && rtc.getHours() > 20)
{
tone(buz,956);
}
// Delay 0.6sec
delay(600);
lcd.clear(); // Clear
}