1.0 of Go version
This commit is contained in:
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
.idea
|
||||
4
.idea/MusicServer.iml
generated
4
.idea/MusicServer.iml
generated
@@ -10,7 +10,9 @@
|
||||
</buildTags>
|
||||
</component>
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$" />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<excludeFolder url="file://$MODULE_DIR$/.idea/dataSources" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
|
||||
11
.idea/dataSources.xml
generated
Normal file
11
.idea/dataSources.xml
generated
Normal file
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="DataSourceManagerImpl" format="xml" multifile-model="true">
|
||||
<data-source source="LOCAL" name="music_dev_local@localhost" uuid="e63caf71-2b1a-400d-9cc4-4b87b1f9b807">
|
||||
<driver-ref>postgresql</driver-ref>
|
||||
<synchronize>true</synchronize>
|
||||
<jdbc-driver>org.postgresql.Driver</jdbc-driver>
|
||||
<jdbc-url>jdbc:postgresql://localhost:5432/music_dev_local</jdbc-url>
|
||||
</data-source>
|
||||
</component>
|
||||
</project>
|
||||
6
.idea/vcs.xml
generated
Normal file
6
.idea/vcs.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
245
.idea/workspace.xml
generated
245
.idea/workspace.xml
generated
@@ -1,25 +1,86 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="BranchesTreeState">
|
||||
<expand>
|
||||
<path>
|
||||
<item name="ROOT" type="e8cecc67:BranchNodeDescriptor" />
|
||||
<item name="LOCAL_ROOT" type="e8cecc67:BranchNodeDescriptor" />
|
||||
</path>
|
||||
<path>
|
||||
<item name="ROOT" type="e8cecc67:BranchNodeDescriptor" />
|
||||
<item name="REMOTE_ROOT" type="e8cecc67:BranchNodeDescriptor" />
|
||||
</path>
|
||||
<path>
|
||||
<item name="ROOT" type="e8cecc67:BranchNodeDescriptor" />
|
||||
<item name="REMOTE_ROOT" type="e8cecc67:BranchNodeDescriptor" />
|
||||
<item name="GROUP_NODE:origin" type="e8cecc67:BranchNodeDescriptor" />
|
||||
</path>
|
||||
</expand>
|
||||
<select />
|
||||
</component>
|
||||
<component name="ChangeListManager">
|
||||
<list default="true" id="d639f8a9-4b6d-4957-9ba4-8361acac15ca" name="Default Changelist" comment="" />
|
||||
<list default="true" id="d639f8a9-4b6d-4957-9ba4-8361acac15ca" name="Default Changelist" comment="">
|
||||
<change afterPath="$PROJECT_DIR$/.gitignore" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/.idea/dataSources.xml" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/.idea/vcs.xml" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/Dockerfile" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/database.go" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/doc/swagger.yaml" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/go.mod" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/init.sh" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/musicFacade.go" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/songs/01. Opening.mp3" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/songs/01. Title.mp3" afterDir="false" />
|
||||
<change afterPath="$PROJECT_DIR$/syncFacade.go" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/app.yaml" beforeDir="false" afterPath="$PROJECT_DIR$/app.yaml" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/musicserver.go" beforeDir="false" afterPath="$PROJECT_DIR$/musicserver.go" afterDir="false" />
|
||||
</list>
|
||||
<option name="SHOW_DIALOG" value="false" />
|
||||
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
||||
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
|
||||
<option name="LAST_RESOLUTION" value="IGNORE" />
|
||||
</component>
|
||||
<component name="GOROOT" path="/usr/local/Cellar/go/1.15.2/libexec" />
|
||||
<component name="FileTemplateManagerImpl">
|
||||
<option name="RECENT_TEMPLATES">
|
||||
<list>
|
||||
<option value="Go File" />
|
||||
</list>
|
||||
</option>
|
||||
</component>
|
||||
<component name="GOROOT" path="/usr/local/Cellar/go/1.15.5/libexec" />
|
||||
<component name="Git.Settings">
|
||||
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
|
||||
</component>
|
||||
<component name="ProjectId" id="1ipVJv6kTmBdByNysh6NxmS21gD" />
|
||||
<component name="ProjectLevelVcsManager" settingsEditedManually="true" />
|
||||
<component name="ProjectViewState">
|
||||
<option name="hideEmptyMiddlePackages" value="true" />
|
||||
<option name="showLibraryContents" value="true" />
|
||||
</component>
|
||||
<component name="PropertiesComponent">
|
||||
<property name="ASKED_SHARE_PROJECT_CONFIGURATION_FILES" value="true" />
|
||||
<property name="DatabaseDriversLRU" value="postgresql" />
|
||||
<property name="DefaultGoTemplateProperty" value="Go File" />
|
||||
<property name="RunOnceActivity.OpenProjectViewOnStart" value="true" />
|
||||
<property name="SHARE_PROJECT_CONFIGURATION_FILES" value="true" />
|
||||
<property name="WebServerToolWindowFactoryState" value="false" />
|
||||
<property name="go.import.settings.migrated" value="true" />
|
||||
<property name="last_opened_file_path" value="$USER_HOME$" />
|
||||
<property name="go.tried.to.enable.integration.vgo.integrator" value="true" />
|
||||
<property name="last_opened_file_path" value="$PROJECT_DIR$/songs" />
|
||||
<property name="node.js.detected.package.eslint" value="true" />
|
||||
<property name="node.js.detected.package.tslint" value="true" />
|
||||
<property name="node.js.path.for.package.eslint" value="project" />
|
||||
<property name="node.js.path.for.package.tslint" value="project" />
|
||||
<property name="node.js.selected.package.eslint" value="(autodetect)" />
|
||||
<property name="node.js.selected.package.tslint" value="(autodetect)" />
|
||||
<property name="restartRequiresConfirmation" value="false" />
|
||||
</component>
|
||||
<component name="RecentsManager">
|
||||
<key name="CopyFile.RECENT_KEYS">
|
||||
<recent name="$PROJECT_DIR$/songs" />
|
||||
</key>
|
||||
</component>
|
||||
<component name="RunManager">
|
||||
<configuration name="App Engine" type="GoApplicationRunConfiguration" factoryName="Go Application">
|
||||
<module name="MusicServer" />
|
||||
@@ -31,27 +92,189 @@
|
||||
<directory value="$PROJECT_DIR$" />
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
<configuration name="Dockerfile" type="docker-deploy" factoryName="dockerfile" temporary="true" server-name="Docker">
|
||||
<deployment type="dockerfile">
|
||||
<settings>
|
||||
<option name="imageTag" value="sansan91/music-server:2.0.0" />
|
||||
<option name="buildCliOptions" value="" />
|
||||
<option name="command" value="" />
|
||||
<option name="containerName" value="music-server-go" />
|
||||
<option name="entrypoint" value="" />
|
||||
<option name="envVars">
|
||||
<list>
|
||||
<DockerEnvVarImpl>
|
||||
<option name="name" value="DB_HOST" />
|
||||
<option name="value" value="ssh.sanplex.xyz" />
|
||||
</DockerEnvVarImpl>
|
||||
<DockerEnvVarImpl>
|
||||
<option name="name" value="DB_USERNAME" />
|
||||
<option name="value" value="postgres" />
|
||||
</DockerEnvVarImpl>
|
||||
<DockerEnvVarImpl>
|
||||
<option name="name" value="DB_PASSWORD" />
|
||||
<option name="value" value="DkrSle9501" />
|
||||
</DockerEnvVarImpl>
|
||||
<DockerEnvVarImpl>
|
||||
<option name="name" value="DB_NAME" />
|
||||
<option name="value" value="music_test" />
|
||||
</DockerEnvVarImpl>
|
||||
</list>
|
||||
</option>
|
||||
<option name="portBindings">
|
||||
<list>
|
||||
<DockerPortBindingImpl>
|
||||
<option name="containerPort" value="8080" />
|
||||
<option name="hostPort" value="8080" />
|
||||
</DockerPortBindingImpl>
|
||||
</list>
|
||||
</option>
|
||||
<option name="commandLineOptions" value="" />
|
||||
<option name="sourceFilePath" value="Dockerfile" />
|
||||
<option name="volumeBindings">
|
||||
<list>
|
||||
<DockerVolumeBindingImpl>
|
||||
<option name="containerPath" value="/sorted" />
|
||||
<option name="hostPath" value="$USER_HOME$/Resilio Sync/Sorterat_test" />
|
||||
</DockerVolumeBindingImpl>
|
||||
<DockerVolumeBindingImpl>
|
||||
<option name="containerPath" value="/doc" />
|
||||
<option name="hostPath" value="$USER_HOME$/doc" />
|
||||
</DockerVolumeBindingImpl>
|
||||
<DockerVolumeBindingImpl>
|
||||
<option name="containerPath" value="/sorterat" />
|
||||
<option name="hostPath" value="$USER_HOME$/Resilio Sync/Sorterat_test" />
|
||||
</DockerVolumeBindingImpl>
|
||||
</list>
|
||||
</option>
|
||||
</settings>
|
||||
</deployment>
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
</component>
|
||||
<component name="SpellCheckerSettings" RuntimeDictionaries="0" Folders="0" CustomDictionaries="0" DefaultDictionary="application-level" UseSingleDictionary="true" transferred="true" />
|
||||
<component name="TypeScriptGeneratedFilesManager">
|
||||
<option name="version" value="3" />
|
||||
</component>
|
||||
<component name="Vcs.Log.Tabs.Properties">
|
||||
<option name="TAB_STATES">
|
||||
<map>
|
||||
<entry key="MAIN">
|
||||
<value>
|
||||
<State />
|
||||
</value>
|
||||
</entry>
|
||||
</map>
|
||||
</option>
|
||||
<option name="oldMeFiltersMigrated" value="true" />
|
||||
</component>
|
||||
<component name="VgoProject">
|
||||
<integration-enabled>true</integration-enabled>
|
||||
</component>
|
||||
<component name="WindowStateProjectService">
|
||||
<state width="1238" height="212" key="GridCell.Tab.0.bottom" timestamp="1602604775177">
|
||||
<state x="432" y="337" key="#GOROOT" timestamp="1606056684244">
|
||||
<screen x="0" y="0" width="1280" height="800" />
|
||||
</state>
|
||||
<state width="1238" height="212" key="GridCell.Tab.0.bottom/0.0.1280.800@0.0.1280.800" timestamp="1602604775177" />
|
||||
<state width="1238" height="212" key="GridCell.Tab.0.center" timestamp="1602604775176">
|
||||
<state x="432" y="337" key="#GOROOT/0.0.1280.800@0.0.1280.800" timestamp="1606056684244" />
|
||||
<state x="440" y="240" key="#Go_Modules" timestamp="1602878032740">
|
||||
<screen x="0" y="0" width="1280" height="800" />
|
||||
</state>
|
||||
<state width="1238" height="212" key="GridCell.Tab.0.center/0.0.1280.800@0.0.1280.800" timestamp="1602604775176" />
|
||||
<state width="1238" height="212" key="GridCell.Tab.0.left" timestamp="1602604775175">
|
||||
<state x="440" y="240" key="#Go_Modules/0.0.1280.800@0.0.1280.800" timestamp="1602878032740" />
|
||||
<state x="209" y="65" key="#com.intellij.execution.impl.EditConfigurationsDialog" timestamp="1606500738678">
|
||||
<screen x="0" y="0" width="1280" height="800" />
|
||||
</state>
|
||||
<state width="1238" height="212" key="GridCell.Tab.0.left/0.0.1280.800@0.0.1280.800" timestamp="1602604775175" />
|
||||
<state width="1238" height="212" key="GridCell.Tab.0.right" timestamp="1602604775177">
|
||||
<state x="209" y="65" key="#com.intellij.execution.impl.EditConfigurationsDialog/0.0.1280.800@0.0.1280.800" timestamp="1606500738678" />
|
||||
<state x="354" y="206" key="#com.intellij.fileTypes.FileTypeChooser" timestamp="1606327049349">
|
||||
<screen x="0" y="0" width="1280" height="800" />
|
||||
</state>
|
||||
<state width="1238" height="212" key="GridCell.Tab.0.right/0.0.1280.800@0.0.1280.800" timestamp="1602604775177" />
|
||||
<state x="354" y="206" key="#com.intellij.fileTypes.FileTypeChooser/0.0.1280.800@0.0.1280.800" timestamp="1606327049349" />
|
||||
<state x="63" y="22" width="1120" height="704" key="CommitChangelistDialog2" timestamp="1606500771742">
|
||||
<screen x="0" y="0" width="1280" height="800" />
|
||||
</state>
|
||||
<state x="63" y="22" width="1120" height="704" key="CommitChangelistDialog2/0.0.1280.800@0.0.1280.800" timestamp="1606500771742" />
|
||||
<state x="500" y="304" key="Github.ShareDialog" timestamp="1602604919305">
|
||||
<screen x="0" y="0" width="1280" height="800" />
|
||||
</state>
|
||||
<state x="500" y="304" key="Github.ShareDialog/0.0.1280.800@0.0.1280.800" timestamp="1602604919305" />
|
||||
<state x="454" y="112" key="Github.UntrackedFilesDialog" timestamp="1602604943862">
|
||||
<screen x="0" y="0" width="1280" height="800" />
|
||||
</state>
|
||||
<state x="454" y="112" key="Github.UntrackedFilesDialog/0.0.1280.800@0.0.1280.800" timestamp="1602604943862" />
|
||||
<state width="1238" height="264" key="GridCell.Tab.0.bottom" timestamp="1606334681949">
|
||||
<screen x="0" y="0" width="1280" height="800" />
|
||||
</state>
|
||||
<state width="1238" height="264" key="GridCell.Tab.0.bottom/0.0.1280.800@0.0.1280.800" timestamp="1606334681949" />
|
||||
<state width="846" height="212" key="GridCell.Tab.0.bottom/0.25.1280.702@0.25.1280.702" timestamp="1606330812762" />
|
||||
<state width="1238" height="264" key="GridCell.Tab.0.center" timestamp="1606334681948">
|
||||
<screen x="0" y="0" width="1280" height="800" />
|
||||
</state>
|
||||
<state width="1238" height="264" key="GridCell.Tab.0.center/0.0.1280.800@0.0.1280.800" timestamp="1606334681948" />
|
||||
<state width="846" height="212" key="GridCell.Tab.0.center/0.25.1280.702@0.25.1280.702" timestamp="1606330812757" />
|
||||
<state width="1238" height="264" key="GridCell.Tab.0.left" timestamp="1606334681947">
|
||||
<screen x="0" y="0" width="1280" height="800" />
|
||||
</state>
|
||||
<state width="1238" height="264" key="GridCell.Tab.0.left/0.0.1280.800@0.0.1280.800" timestamp="1606334681947" />
|
||||
<state width="846" height="212" key="GridCell.Tab.0.left/0.25.1280.702@0.25.1280.702" timestamp="1606330812754" />
|
||||
<state width="1238" height="264" key="GridCell.Tab.0.right" timestamp="1606334681949">
|
||||
<screen x="0" y="0" width="1280" height="800" />
|
||||
</state>
|
||||
<state width="1238" height="264" key="GridCell.Tab.0.right/0.0.1280.800@0.0.1280.800" timestamp="1606334681949" />
|
||||
<state width="846" height="212" key="GridCell.Tab.0.right/0.25.1280.702@0.25.1280.702" timestamp="1606330812760" />
|
||||
<state width="1238" height="264" key="GridCell.Tab.1.bottom" timestamp="1606334681952">
|
||||
<screen x="0" y="0" width="1280" height="800" />
|
||||
</state>
|
||||
<state width="1238" height="264" key="GridCell.Tab.1.bottom/0.0.1280.800@0.0.1280.800" timestamp="1606334681952" />
|
||||
<state width="1238" height="264" key="GridCell.Tab.1.bottom/0.25.1280.702@0.25.1280.702" timestamp="1606330741478" />
|
||||
<state width="1238" height="264" key="GridCell.Tab.1.center" timestamp="1606334681950">
|
||||
<screen x="0" y="0" width="1280" height="800" />
|
||||
</state>
|
||||
<state width="1238" height="264" key="GridCell.Tab.1.center/0.0.1280.800@0.0.1280.800" timestamp="1606334681950" />
|
||||
<state width="1238" height="264" key="GridCell.Tab.1.center/0.25.1280.702@0.25.1280.702" timestamp="1606330741474" />
|
||||
<state width="1238" height="264" key="GridCell.Tab.1.left" timestamp="1606334681950">
|
||||
<screen x="0" y="0" width="1280" height="800" />
|
||||
</state>
|
||||
<state width="1238" height="264" key="GridCell.Tab.1.left/0.0.1280.800@0.0.1280.800" timestamp="1606334681950" />
|
||||
<state width="1238" height="264" key="GridCell.Tab.1.left/0.25.1280.702@0.25.1280.702" timestamp="1606330741473" />
|
||||
<state width="1238" height="264" key="GridCell.Tab.1.right" timestamp="1606334681951">
|
||||
<screen x="0" y="0" width="1280" height="800" />
|
||||
</state>
|
||||
<state width="1238" height="264" key="GridCell.Tab.1.right/0.0.1280.800@0.0.1280.800" timestamp="1606334681951" />
|
||||
<state width="1238" height="264" key="GridCell.Tab.1.right/0.25.1280.702@0.25.1280.702" timestamp="1606330741474" />
|
||||
<state x="491" y="322" key="VCS.ChangelistChooser" timestamp="1604854207192">
|
||||
<screen x="0" y="0" width="1280" height="800" />
|
||||
</state>
|
||||
<state x="491" y="322" key="VCS.ChangelistChooser/0.0.1280.800@0.0.1280.800" timestamp="1604854207192" />
|
||||
<state x="546" y="264" key="com.intellij.docker.deployment.ui.DockerPortBindingsField.v173.dialogSize" timestamp="1606060189431">
|
||||
<screen x="0" y="0" width="1280" height="800" />
|
||||
</state>
|
||||
<state x="546" y="264" key="com.intellij.docker.deployment.ui.DockerPortBindingsField.v173.dialogSize/0.0.1280.800@0.0.1280.800" timestamp="1606060189431" />
|
||||
<state x="361" y="264" width="706" height="259" key="com.intellij.docker.deployment.ui.DockerVolumeBindingsField.v173.dialogSize" timestamp="1606334614894">
|
||||
<screen x="0" y="0" width="1280" height="800" />
|
||||
</state>
|
||||
<state x="361" y="264" width="706" height="259" key="com.intellij.docker.deployment.ui.DockerVolumeBindingsField.v173.dialogSize/0.0.1280.800@0.0.1280.800" timestamp="1606334614894" />
|
||||
<state x="483" y="173" key="refactoring.ChangeSignatureDialog" timestamp="1603014849046">
|
||||
<screen x="0" y="0" width="1280" height="800" />
|
||||
</state>
|
||||
<state x="483" y="173" key="refactoring.ChangeSignatureDialog/0.0.1280.800@0.0.1280.800" timestamp="1603014849046" />
|
||||
<state x="305" y="124" width="670" height="676" key="search.everywhere.popup" timestamp="1602967169862">
|
||||
<screen x="0" y="0" width="1280" height="800" />
|
||||
</state>
|
||||
<state x="305" y="124" width="670" height="676" key="search.everywhere.popup/0.0.1280.800@0.0.1280.800" timestamp="1602967169862" />
|
||||
</component>
|
||||
<component name="XDebuggerManager">
|
||||
<breakpoint-manager>
|
||||
<breakpoints>
|
||||
<line-breakpoint enabled="true" type="DlvLineBreakpoint">
|
||||
<url>file://$PROJECT_DIR$/musicFacade.go</url>
|
||||
<line>139</line>
|
||||
<option name="timeStamp" value="4" />
|
||||
</line-breakpoint>
|
||||
</breakpoints>
|
||||
</breakpoint-manager>
|
||||
<watches-manager>
|
||||
<configuration name="GoApplicationRunConfiguration">
|
||||
<watch expression="games" language="go" />
|
||||
<watch expression="songQue" language="go" />
|
||||
</configuration>
|
||||
</watches-manager>
|
||||
</component>
|
||||
</project>
|
||||
27
Dockerfile
Normal file
27
Dockerfile
Normal file
@@ -0,0 +1,27 @@
|
||||
FROM golang:1.15.5-alpine as build_go
|
||||
|
||||
COPY *.go go.mod /music-server/
|
||||
WORKDIR /music-server
|
||||
RUN go build
|
||||
|
||||
# Stage 2, distribution container
|
||||
FROM golang:1.15.5-alpine
|
||||
RUN apk add --no-cache bash
|
||||
EXPOSE 8080
|
||||
VOLUME /sorted
|
||||
VOLUME /sorterad
|
||||
VOLUME /doc
|
||||
|
||||
ENV DB_HOST ""
|
||||
ENV DB_PORT ""
|
||||
ENV DB_USERNAME ""
|
||||
ENV DB_PASSWORD ""
|
||||
ENV DB_NAME ""
|
||||
|
||||
COPY --from=build_go /music-server/MusicServer .
|
||||
COPY ./doc/swagger.yaml .
|
||||
COPY ./songs/ ./songs/
|
||||
COPY ./init.sh .
|
||||
RUN chmod 777 ./init.sh
|
||||
|
||||
CMD ./init.sh
|
||||
2
app.yaml
2
app.yaml
@@ -1,5 +1,5 @@
|
||||
application: musicserver
|
||||
version: 1
|
||||
version: 2.0.0
|
||||
runtime: go115
|
||||
api_version: go1
|
||||
|
||||
|
||||
244
database.go
Normal file
244
database.go
Normal file
@@ -0,0 +1,244 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/jackc/pgtype"
|
||||
"github.com/jackc/pgx/v4/pgxpool"
|
||||
"os"
|
||||
"time"
|
||||
)
|
||||
|
||||
var dbPool *pgxpool.Pool
|
||||
|
||||
func initDB(host string, port int, user string, password string, dbname string) {
|
||||
|
||||
psqlInfo := fmt.Sprintf("host=%s port=%d user=%s "+
|
||||
"password=%s dbname=%s sslmode=disable",
|
||||
host, port, user, password, dbname)
|
||||
|
||||
fmt.Println(psqlInfo)
|
||||
|
||||
var err error
|
||||
dbPool, err = pgxpool.Connect(context.Background(), psqlInfo)
|
||||
if err != nil {
|
||||
_, _ = fmt.Fprintf(os.Stderr, "Unable to connect to database: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
var success string
|
||||
err = dbPool.QueryRow(context.Background(), "select 'Successfully connected!'").Scan(&success)
|
||||
if err != nil {
|
||||
_, _ = fmt.Fprintf(os.Stderr, "QueryRow failed: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Println(success)
|
||||
}
|
||||
|
||||
func closeDb() {
|
||||
dbPool.Close()
|
||||
}
|
||||
|
||||
func getGameName(gameId int) string {
|
||||
var gameName = ""
|
||||
err := dbPool.QueryRow(context.Background(),
|
||||
"SELECT game_name FROM game WHERE id = $1", gameId).Scan(&gameName)
|
||||
if err != nil {
|
||||
_, _ = fmt.Fprintf(os.Stderr, "QueryRow failed: %v\n", err)
|
||||
return ""
|
||||
}
|
||||
return gameName
|
||||
}
|
||||
|
||||
func setGameDeletionDate() {
|
||||
_, err := dbPool.Exec(context.Background(),
|
||||
"UPDATE game SET deleted=$1", time.Now())
|
||||
if err != nil {
|
||||
_, _ = fmt.Fprintf(os.Stderr, "Exec failed: %v\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
func clearSongs(gameId int) {
|
||||
if gameId == -1 {
|
||||
_, err := dbPool.Exec(context.Background(), "DELETE FROM song")
|
||||
if err != nil {
|
||||
_, _ = fmt.Fprintf(os.Stderr, "Exec failed: %v\n", err)
|
||||
}
|
||||
} else {
|
||||
_, err := dbPool.Exec(context.Background(), "DELETE FROM song where game_id=$1", gameId)
|
||||
if err != nil {
|
||||
_, _ = fmt.Fprintf(os.Stderr, "Exec failed: %v\n", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func clearGames() {
|
||||
_, err := dbPool.Exec(context.Background(), "DELETE FROM game")
|
||||
if err != nil {
|
||||
_, _ = fmt.Fprintf(os.Stderr, "Exec failed: %v\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
func updateGameName(id int, name string, path string) {
|
||||
_, err := dbPool.Exec(context.Background(),
|
||||
"UPDATE game SET game_name=$1, path=$2, last_changed=$3 WHERE id=$4",
|
||||
name, path, time.Now(), id)
|
||||
if err != nil {
|
||||
_, _ = fmt.Fprintf(os.Stderr, "Exec failed: %v\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
func removeDeletionDate(id int) {
|
||||
_, err := dbPool.Exec(context.Background(),
|
||||
"UPDATE game SET deleted=null WHERE id=$1", id)
|
||||
if err != nil {
|
||||
_, _ = fmt.Fprintf(os.Stderr, "Exec failed: %v\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
func addSong(song SongData) {
|
||||
_, err := dbPool.Exec(context.Background(),
|
||||
"INSERT INTO song(game_id, song_name, path) VALUES ($1, $2, $3)",
|
||||
song.gameId, song.songName, song.path)
|
||||
if err != nil {
|
||||
_, _ = fmt.Fprintf(os.Stderr, "Exec failed: %v\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
func findSongsFromGame(id int) []SongData {
|
||||
rows, err := dbPool.Query(context.Background(),
|
||||
"SELECT song_name, path, times_played FROM song WHERE game_id = $1", id)
|
||||
if err != nil {
|
||||
_, _ = fmt.Fprintf(os.Stderr, "QueryRow failed: %v\n", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
var songDataList []SongData
|
||||
for rows.Next() {
|
||||
var songName string
|
||||
var path string
|
||||
var timesPlayed int
|
||||
|
||||
err := rows.Scan(&songName, &path, ×Played)
|
||||
if err != nil {
|
||||
_, _ = fmt.Fprintf(os.Stderr, "QueryRow failed: %v\n", err)
|
||||
}
|
||||
|
||||
songDataList = append(songDataList, SongData{
|
||||
gameId: id,
|
||||
songName: songName,
|
||||
path: path,
|
||||
timesPlayed: timesPlayed,
|
||||
})
|
||||
}
|
||||
return songDataList
|
||||
}
|
||||
|
||||
func getIdByGameName(name string) int {
|
||||
var gameId = -1
|
||||
err := dbPool.QueryRow(context.Background(),
|
||||
"SELECT id FROM game WHERE game_name = $1", name).Scan(&gameId)
|
||||
if err != nil {
|
||||
_, _ = fmt.Fprintf(os.Stderr, "QueryRow failed: %v\n", err)
|
||||
return -1
|
||||
}
|
||||
return gameId
|
||||
}
|
||||
|
||||
func insertGame(name string, path string) int {
|
||||
gameId := -1
|
||||
err := dbPool.QueryRow(context.Background(),
|
||||
"INSERT INTO game(game_name, path, added) VALUES ($1, $2, $3) RETURNING id",
|
||||
name, path, time.Now()).Scan(&gameId)
|
||||
if err != nil {
|
||||
_, _ = fmt.Fprintf(os.Stderr, "QueryRow failed: %v\n", err)
|
||||
resetGameIdSeq()
|
||||
err2 := dbPool.QueryRow(context.Background(),
|
||||
"INSERT INTO game(game_name, path, added) VALUES ($1, $2, $3) RETURNING id",
|
||||
name, path, time.Now()).Scan(&gameId)
|
||||
if err2 != nil {
|
||||
_, _ = fmt.Fprintf(os.Stderr, "QueryRow failed: %v\n", err)
|
||||
return -1
|
||||
}
|
||||
}
|
||||
return gameId
|
||||
}
|
||||
|
||||
func insertGameWithExistingId(id int, name string, path string) {
|
||||
_, err := dbPool.Exec(context.Background(),
|
||||
"INSERT INTO game(id, game_name, path, added) VALUES ($1, $2, $3, $4)",
|
||||
id, name, path, time.Now())
|
||||
if err != nil {
|
||||
_, _ = fmt.Fprintf(os.Stderr, "Exec failed: %v\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
func resetGameIdSeq() {
|
||||
_, err := dbPool.Query(context.Background(), "SELECT setval('game_id_seq', (SELECT MAX(id) FROM game)+1);")
|
||||
if err != nil {
|
||||
_, _ = fmt.Fprintf(os.Stderr, "Exec failed: %v\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
func findAllGames() []GameData {
|
||||
rows, err := dbPool.Query(context.Background(),
|
||||
"SELECT id, game_name, added, deleted, last_changed, path, times_played, last_played, number_of_songs FROM game")
|
||||
if err != nil {
|
||||
_, _ = fmt.Fprintf(os.Stderr, "QueryRow failed: %v\n", err)
|
||||
}
|
||||
var gameList []GameData
|
||||
for rows.Next() {
|
||||
var id, timesPlayed int
|
||||
var numberOfSongs pgtype.Int4
|
||||
var gameName, path string
|
||||
var added, deleted, lastChanged, lastPlayed pgtype.Timestamp
|
||||
err := rows.Scan(&id, &gameName, &added, &deleted, &lastChanged, &path, ×Played, &lastPlayed, &numberOfSongs)
|
||||
if err != nil {
|
||||
_, _ = fmt.Fprintf(os.Stderr, "QueryRow failed: %v\n", err)
|
||||
}
|
||||
gameList = append(gameList, GameData{
|
||||
id: id,
|
||||
gameName: gameName,
|
||||
added: added.Time,
|
||||
deleted: deleted.Time,
|
||||
lastChanged: lastChanged.Time,
|
||||
path: path,
|
||||
timesPlayed: timesPlayed,
|
||||
lastPlayed: lastPlayed.Time,
|
||||
numberOfSongs: numberOfSongs.Int,
|
||||
})
|
||||
}
|
||||
return gameList
|
||||
}
|
||||
|
||||
func addGamePlayed(id int) {
|
||||
_, err := dbPool.Exec(context.Background(),
|
||||
"UPDATE game SET times_played=times_played+1, last_played=now() WHERE id=$1", id)
|
||||
if err != nil {
|
||||
_, _ = fmt.Fprintf(os.Stderr, "Exec failed: %v\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
func addSongPlayed(id int, name string) {
|
||||
_, err := dbPool.Exec(context.Background(),
|
||||
"UPDATE song SET times_played=times_played+1 WHERE game_id=$1 AND song_name=$2", id, name)
|
||||
if err != nil {
|
||||
_, _ = fmt.Fprintf(os.Stderr, "Exec failed: %v\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
func testf() {
|
||||
rows, dbErr := dbPool.Query(context.Background(), "select game_name from game")
|
||||
if dbErr != nil {
|
||||
_, _ = fmt.Fprintf(os.Stderr, "QueryRow failed: %v\n", dbErr)
|
||||
os.Exit(1)
|
||||
}
|
||||
for rows.Next() {
|
||||
var gameName string
|
||||
dbErr = rows.Scan(&gameName)
|
||||
if dbErr != nil {
|
||||
_, _ = fmt.Fprintf(os.Stderr, "QueryRow failed: %v\n", dbErr)
|
||||
}
|
||||
_, _ = fmt.Fprintf(os.Stderr, "%v\n", gameName)
|
||||
}
|
||||
}
|
||||
290
doc/swagger.yaml
Normal file
290
doc/swagger.yaml
Normal file
@@ -0,0 +1,290 @@
|
||||
openapi: 3.0.3
|
||||
info:
|
||||
version: "2.0.0"
|
||||
title: "Music Server"
|
||||
description: "Rebuilt the application in Go."
|
||||
contact:
|
||||
email: "zarnor91@gmail.com"
|
||||
servers:
|
||||
- url: https://music.sanplex.xyz/
|
||||
description: Main (production) server
|
||||
tags:
|
||||
- name: "Music"
|
||||
description: "Endpoints for the music"
|
||||
- name: "Version"
|
||||
description: "Information about the application"
|
||||
- name: "Sync"
|
||||
description: "Sync the database with the folders"
|
||||
paths:
|
||||
/music:
|
||||
get:
|
||||
tags:
|
||||
- "Music"
|
||||
summary: "Get song"
|
||||
description: "Get a specified song from the que, if invalid number it will be the first or the last song in que"
|
||||
operationId: "getSong"
|
||||
parameters:
|
||||
- name: "song"
|
||||
in: "query"
|
||||
schema:
|
||||
type: integer
|
||||
description: "The number the song has in the que"
|
||||
required: true
|
||||
responses:
|
||||
"200":
|
||||
description: "The speciefied file"
|
||||
content:
|
||||
audio/mpeg:
|
||||
schema:
|
||||
type: object
|
||||
format: binary
|
||||
"500":
|
||||
description: "Something wnet wrong on the server"
|
||||
/music/first:
|
||||
get:
|
||||
tags:
|
||||
- "Music"
|
||||
summary: "Start a match"
|
||||
description: "Get a sound check song and starts a new song que"
|
||||
operationId: "getFisrt"
|
||||
responses:
|
||||
"200":
|
||||
description: "A file"
|
||||
content:
|
||||
audio/mpeg:
|
||||
schema:
|
||||
type: object
|
||||
format: binary
|
||||
"500":
|
||||
description: "Something wnet wrong on the server"
|
||||
/music/rand:
|
||||
get:
|
||||
tags:
|
||||
- "Music"
|
||||
summary: "Get random song"
|
||||
description: "Takes a random song from a random game"
|
||||
operationId: "getRandom"
|
||||
responses:
|
||||
"200":
|
||||
description: "A file"
|
||||
content:
|
||||
audio/mpeg:
|
||||
schema:
|
||||
type: object
|
||||
format: binary
|
||||
"500":
|
||||
description: "Something wnet wrong on the server"
|
||||
/music/rand/low:
|
||||
get:
|
||||
tags:
|
||||
- "Music"
|
||||
summary: "Get random song"
|
||||
description: "Takes a random song from a random game but increases the chans for games that haven't been played"
|
||||
operationId: "getRandomLow"
|
||||
responses:
|
||||
"200":
|
||||
description: "A file"
|
||||
content:
|
||||
audio/mpeg:
|
||||
schema:
|
||||
type: object
|
||||
format: binary
|
||||
"500":
|
||||
description: "Something wnet wrong on the server"
|
||||
/music/info:
|
||||
get:
|
||||
tags:
|
||||
- "Music"
|
||||
summary: "Get info of current song"
|
||||
description: "Return the info of the song currently playing"
|
||||
operationId: "getInfo"
|
||||
responses:
|
||||
"200":
|
||||
description: "Info of current song"
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/info'
|
||||
"500":
|
||||
description: "Something wnet wrong on the server"
|
||||
/music/list:
|
||||
get:
|
||||
tags:
|
||||
- "Music"
|
||||
summary: "Gets song que"
|
||||
description: "Gets a list of all songs that have been fetched"
|
||||
operationId: "getList"
|
||||
responses:
|
||||
"200":
|
||||
description: "A list"
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/list'
|
||||
"500":
|
||||
description: "Something went wrong on the server"
|
||||
/music/next:
|
||||
get:
|
||||
tags:
|
||||
- "Music"
|
||||
summary: "Get next song"
|
||||
description: "Gets next song in the song que, if there is no more a new random song will be generated."
|
||||
operationId: "getNext"
|
||||
responses:
|
||||
"200":
|
||||
description: "A file"
|
||||
content:
|
||||
audio/mpeg:
|
||||
schema:
|
||||
type: object
|
||||
format: binary
|
||||
"500":
|
||||
description: "Something wnet wrong on the server"
|
||||
/music/previous:
|
||||
get:
|
||||
tags:
|
||||
- "Music"
|
||||
summary: "Get previous song"
|
||||
description: "Gets previous song in the song que, if there is no more the first song will be returns."
|
||||
operationId: "getPrevious"
|
||||
responses:
|
||||
"200":
|
||||
description: "A file"
|
||||
content:
|
||||
audio/mpeg:
|
||||
schema:
|
||||
type: object
|
||||
format: binary
|
||||
"500":
|
||||
description: "Something wnet wrong on the server"
|
||||
/music/all:
|
||||
get:
|
||||
tags:
|
||||
- "Music"
|
||||
summary: "Gets all games"
|
||||
description: "Gets a list of all games that is in the databse"
|
||||
operationId: "getAll"
|
||||
responses:
|
||||
"200":
|
||||
description: "A list"
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
example: ["God of War", "Final Fantasy VII"]
|
||||
"500":
|
||||
description: "Something went wrong on the server"
|
||||
/music/played:
|
||||
put:
|
||||
tags:
|
||||
- "Music"
|
||||
summary: "Increase played"
|
||||
description: "Increase the number of times the current game has been played"
|
||||
operationId: "setPlayed"
|
||||
responses:
|
||||
"202":
|
||||
description: "Accepted"
|
||||
"401":
|
||||
description: "Bad Request"
|
||||
"500":
|
||||
description: "Something went wrong on the server"
|
||||
/music/reset:
|
||||
get:
|
||||
tags:
|
||||
- "Music"
|
||||
summary: "Resets the song que"
|
||||
description: "Resets the song que for a new match"
|
||||
operationId: "reset"
|
||||
responses:
|
||||
"200":
|
||||
description: "successful operation"
|
||||
"500":
|
||||
description: "Something went wrong on the server"
|
||||
/version:
|
||||
get:
|
||||
tags:
|
||||
- "Version"
|
||||
summary: "Returns pet inventories by status"
|
||||
description: "Returns a map of status codes to quantities"
|
||||
operationId: "getVersionInfo"
|
||||
responses:
|
||||
"200":
|
||||
description: "successful operation"
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/versionInfo'
|
||||
/sync:
|
||||
get:
|
||||
tags:
|
||||
- "Sync"
|
||||
summary: "Sync games"
|
||||
description: "Sync the database with the folders"
|
||||
operationId: "sync"
|
||||
responses:
|
||||
"200":
|
||||
description: "successful operation"
|
||||
"500":
|
||||
description: "Something went wrong on the server"
|
||||
components:
|
||||
schemas:
|
||||
list:
|
||||
type: object
|
||||
properties:
|
||||
Game:
|
||||
type: string
|
||||
example: God of War
|
||||
Song:
|
||||
type: string
|
||||
example: Main Theme.mp3
|
||||
CurrentlyPlaying:
|
||||
type: boolean
|
||||
example: true
|
||||
description: Only included on the one that's playing
|
||||
SongNo:
|
||||
type: integer
|
||||
example: 3
|
||||
info:
|
||||
type: object
|
||||
properties:
|
||||
Game:
|
||||
type: string
|
||||
example: God of War
|
||||
GamePlayed:
|
||||
type: integer
|
||||
example: 10
|
||||
Song:
|
||||
type: string
|
||||
example: Main Theme.mp3
|
||||
SongPlayed:
|
||||
type: integer
|
||||
example: 10
|
||||
SongNo:
|
||||
type: integer
|
||||
example: 3
|
||||
versionInfo:
|
||||
type: object
|
||||
properties:
|
||||
version:
|
||||
type: string
|
||||
example: 0.5.1
|
||||
changelog:
|
||||
type: string
|
||||
example: "Added shadow to build fat jars. Added support to give property files through arguments."
|
||||
history:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/historyInfo'
|
||||
historyInfo:
|
||||
type: object
|
||||
properties:
|
||||
version:
|
||||
type: string
|
||||
example: 0.5.0
|
||||
changelog:
|
||||
type: string
|
||||
example: "Updated kotlin version, gradle version and al libs. Added connection to database with exposed. Added new endpoints."
|
||||
9
go.mod
Normal file
9
go.mod
Normal file
@@ -0,0 +1,9 @@
|
||||
module MusicServer
|
||||
|
||||
go 1.15
|
||||
|
||||
require (
|
||||
github.com/gorilla/mux v1.8.0
|
||||
github.com/jackc/pgtype v1.5.0
|
||||
github.com/jackc/pgx/v4 v4.9.0
|
||||
)
|
||||
4
init.sh
Normal file
4
init.sh
Normal file
@@ -0,0 +1,4 @@
|
||||
#! /bin/sh
|
||||
mkdir /doc
|
||||
cp swagger.yaml /doc
|
||||
./MusicServer
|
||||
243
musicFacade.go
Normal file
243
musicFacade.go
Normal file
@@ -0,0 +1,243 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"math/rand"
|
||||
"net/http"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
var currentSong = -1
|
||||
var games []GameData
|
||||
var songQue []SongData
|
||||
|
||||
func getSoundCheckSong() string {
|
||||
reset()
|
||||
|
||||
files, err := ioutil.ReadDir("songs")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
fileInfo := files[rand.Intn(len(files))]
|
||||
return "songs/" + fileInfo.Name()
|
||||
}
|
||||
|
||||
func reset() {
|
||||
songQue = nil
|
||||
currentSong = -1
|
||||
games = findAllGames()
|
||||
}
|
||||
|
||||
func getRandomSong() string {
|
||||
if games == nil || len(games) == 0 {
|
||||
games = findAllGames()
|
||||
}
|
||||
|
||||
game := getRandomGame(games)
|
||||
|
||||
songs := findSongsFromGame(game.id)
|
||||
|
||||
song := songs[rand.Intn(len(songs))]
|
||||
|
||||
currentSong = len(songQue)
|
||||
songQue = append(songQue, song)
|
||||
return song.path
|
||||
}
|
||||
|
||||
func getRandomSongLowChance() string {
|
||||
gameList := findAllGames()
|
||||
|
||||
var listOfGames []GameData
|
||||
|
||||
var averagePlayed = getAveragePlayed(gameList)
|
||||
|
||||
for _, data := range gameList {
|
||||
var timesToAdd = averagePlayed - data.timesPlayed
|
||||
if timesToAdd <= 0 {
|
||||
listOfGames = append(listOfGames, data)
|
||||
} else {
|
||||
for i := 0; i < timesToAdd; i++ {
|
||||
listOfGames = append(listOfGames, data)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
game := getRandomGame(listOfGames)
|
||||
|
||||
songs := findSongsFromGame(game.id)
|
||||
|
||||
song := songs[rand.Intn(len(songs))]
|
||||
|
||||
currentSong = len(songQue)
|
||||
songQue = append(songQue, song)
|
||||
return song.path
|
||||
|
||||
}
|
||||
|
||||
func getSongInfo() SongInfo {
|
||||
var currentSongData = songQue[currentSong]
|
||||
|
||||
currentGameData := getCurrentGame(currentSongData)
|
||||
|
||||
return SongInfo{
|
||||
Game: currentGameData.gameName,
|
||||
GamePlayed: currentGameData.timesPlayed,
|
||||
Song: currentSongData.songName,
|
||||
SongPlayed: currentSongData.timesPlayed,
|
||||
CurrentlyPlaying: true,
|
||||
SongNo: currentSong,
|
||||
}
|
||||
}
|
||||
|
||||
func getPlayedSongs() []SongInfo {
|
||||
var songList []SongInfo
|
||||
|
||||
for i, song := range songQue {
|
||||
gameData := getCurrentGame(song)
|
||||
songList = append(songList, SongInfo{
|
||||
Game: gameData.gameName,
|
||||
GamePlayed: gameData.timesPlayed,
|
||||
Song: song.songName,
|
||||
SongPlayed: song.timesPlayed,
|
||||
CurrentlyPlaying: i == currentSong,
|
||||
SongNo: i,
|
||||
})
|
||||
}
|
||||
return songList
|
||||
}
|
||||
|
||||
func getSong(song string) string {
|
||||
currentSong, _ = strconv.Atoi(song)
|
||||
if currentSong >= len(songQue) {
|
||||
currentSong = len(songQue) - 1
|
||||
} else if currentSong < 0 {
|
||||
currentSong = 0
|
||||
}
|
||||
var songData = songQue[currentSong]
|
||||
return songData.path
|
||||
}
|
||||
|
||||
func getAllGames() []string {
|
||||
if games == nil || len(games) == 0 {
|
||||
games = findAllGames()
|
||||
}
|
||||
|
||||
var jsonArray []string
|
||||
for _, game := range games {
|
||||
jsonArray = append(jsonArray, game.gameName)
|
||||
}
|
||||
return jsonArray
|
||||
}
|
||||
|
||||
func setPlayed(songNumber int) {
|
||||
if songQue == nil || len(songQue) == 0 || songNumber >= len(songQue) {
|
||||
return
|
||||
}
|
||||
var songData = songQue[songNumber]
|
||||
addGamePlayed(songData.gameId)
|
||||
addSongPlayed(songData.gameId, songData.songName)
|
||||
}
|
||||
|
||||
func getNextSong() string {
|
||||
if currentSong == len(songQue)-1 || currentSong == -1 {
|
||||
return getRandomSong()
|
||||
} else {
|
||||
currentSong = currentSong + 1
|
||||
var songData = songQue[currentSong]
|
||||
return songData.path
|
||||
}
|
||||
}
|
||||
|
||||
func getPreviousSong() string {
|
||||
if currentSong == -1 || currentSong == 0 {
|
||||
var songData = songQue[0]
|
||||
return songData.path
|
||||
} else {
|
||||
currentSong = currentSong - 1
|
||||
var songData = songQue[currentSong]
|
||||
return songData.path
|
||||
}
|
||||
}
|
||||
|
||||
func musicHandler(w http.ResponseWriter, r *http.Request) {
|
||||
if r.URL.Path == "/music" && r.Method == http.MethodGet {
|
||||
song := r.URL.Query().Get("song")
|
||||
if song == "" {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
_, err := fmt.Fprint(w, "song can't be empty")
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
}
|
||||
} else {
|
||||
http.ServeFile(w, r, getSong(song))
|
||||
}
|
||||
} else if r.URL.Path == "/music/first" && r.Method == http.MethodGet {
|
||||
http.ServeFile(w, r, getSoundCheckSong())
|
||||
|
||||
} else if r.URL.Path == "/music/reset" && r.Method == http.MethodGet {
|
||||
reset()
|
||||
w.WriteHeader(http.StatusOK)
|
||||
|
||||
} else if r.URL.Path == "/music/rand" && r.Method == http.MethodGet {
|
||||
http.ServeFile(w, r, getRandomSong())
|
||||
|
||||
} else if r.URL.Path == "/music/rand/low" && r.Method == http.MethodGet {
|
||||
http.ServeFile(w, r, getRandomSongLowChance())
|
||||
|
||||
} else if r.URL.Path == "/music/info" && r.Method == http.MethodGet {
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
_ = json.NewEncoder(w).Encode(getSongInfo())
|
||||
|
||||
} else if r.URL.Path == "/music/list" && r.Method == http.MethodGet {
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
_ = json.NewEncoder(w).Encode(getPlayedSongs())
|
||||
|
||||
} else if r.URL.Path == "/music/next" {
|
||||
http.ServeFile(w, r, getNextSong())
|
||||
|
||||
} else if r.URL.Path == "/music/previous" {
|
||||
http.ServeFile(w, r, getPreviousSong())
|
||||
|
||||
} else if r.URL.Path == "/music/all" && r.Method == http.MethodGet {
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
_ = json.NewEncoder(w).Encode(getAllGames())
|
||||
|
||||
} else if r.URL.Path == "/music/played" && r.Method == http.MethodPut {
|
||||
var p Played
|
||||
err := json.NewDecoder(r.Body).Decode(&p)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
setPlayed(p.song)
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}
|
||||
}
|
||||
|
||||
type Played struct {
|
||||
song int
|
||||
}
|
||||
|
||||
func getCurrentGame(currentSongData SongData) GameData {
|
||||
for _, game := range games {
|
||||
if game.id == currentSongData.gameId {
|
||||
return game
|
||||
}
|
||||
}
|
||||
return GameData{}
|
||||
}
|
||||
|
||||
func getAveragePlayed(gameList []GameData) int {
|
||||
var sum int
|
||||
for _, data := range gameList {
|
||||
sum += data.timesPlayed
|
||||
}
|
||||
return sum / len(gameList)
|
||||
}
|
||||
|
||||
func getRandomGame(listOfGames []GameData) GameData {
|
||||
return listOfGames[rand.Intn(len(listOfGames))]
|
||||
}
|
||||
135
musicserver.go
135
musicserver.go
@@ -1,14 +1,59 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/gorilla/mux"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
http.HandleFunc("/", indexHandler)
|
||||
// Get the value of an Environment Variable
|
||||
host := os.Getenv("DB_HOST")
|
||||
dbPort, dbPortErr := strconv.Atoi(os.Getenv("DB_PORT"))
|
||||
if dbPortErr != nil {
|
||||
dbPort = 0
|
||||
}
|
||||
username := os.Getenv("DB_USERNAME")
|
||||
password := os.Getenv("DB_PASSWORD")
|
||||
dbName := os.Getenv("DB_NAME")
|
||||
|
||||
fmt.Printf("host: %s, dbPort: %v, username: %s, password: %s, dbName: %s\n",
|
||||
host, dbPort, username, password, dbName)
|
||||
|
||||
if host == "" {
|
||||
host = "localhost"
|
||||
}
|
||||
if dbPort == 0 {
|
||||
dbPort = 5432
|
||||
}
|
||||
if username == "" {
|
||||
username = "sebastian"
|
||||
}
|
||||
if password == "" {
|
||||
password = "950100"
|
||||
}
|
||||
if dbName == "" {
|
||||
dbName = "music_dev_local"
|
||||
}
|
||||
|
||||
initDB(host, dbPort, username, password, dbName)
|
||||
defer closeDb()
|
||||
|
||||
r := mux.NewRouter()
|
||||
r.HandleFunc("/sync", syncHandler)
|
||||
r.HandleFunc("/sync/{func}", syncHandler)
|
||||
r.HandleFunc("/music", musicHandler)
|
||||
r.HandleFunc("/music/{func}", musicHandler)
|
||||
r.HandleFunc("/music/{func}/{func2}", musicHandler)
|
||||
r.HandleFunc("/{func}", indexHandler)
|
||||
http.Handle("/", r)
|
||||
|
||||
port := os.Getenv("PORT")
|
||||
if port == "" {
|
||||
port = "8080"
|
||||
@@ -20,13 +65,91 @@ func main() {
|
||||
log.Fatal(http.ListenAndServe(fmt.Sprintf(":%s", port), nil))
|
||||
}
|
||||
|
||||
type VersionData struct {
|
||||
Version string `json:"version"`
|
||||
Changelog string `json:"changelog"`
|
||||
History []VersionData `json:"history"`
|
||||
}
|
||||
|
||||
type SongInfo struct {
|
||||
Game string `json:"Game"`
|
||||
GamePlayed int `json:"GamePlayed"`
|
||||
Song string `json:"Song"`
|
||||
SongPlayed int `json:"SongPlayed"`
|
||||
CurrentlyPlaying bool `json:"CurrentlyPlaying"`
|
||||
SongNo int `json:"SongNo"`
|
||||
}
|
||||
|
||||
type GameData struct {
|
||||
id int
|
||||
gameName string
|
||||
added time.Time
|
||||
deleted time.Time
|
||||
lastChanged time.Time
|
||||
path string
|
||||
timesPlayed int
|
||||
lastPlayed time.Time
|
||||
numberOfSongs int32
|
||||
}
|
||||
|
||||
type SongData struct {
|
||||
gameId int
|
||||
songName string
|
||||
path string
|
||||
timesPlayed int
|
||||
}
|
||||
|
||||
func indexHandler(w http.ResponseWriter, r *http.Request) {
|
||||
if r.URL.Path == "/test" {
|
||||
_, err := fmt.Fprint(w, "Testing path")
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
if r.URL.Path == "/version" {
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
|
||||
testf()
|
||||
|
||||
data := VersionData{Version: "2.0.0",
|
||||
Changelog: "Rebuilt the application in Go.",
|
||||
History: []VersionData{
|
||||
{
|
||||
Version: "1.2.0",
|
||||
Changelog: "Made the /sync endpoint async. " +
|
||||
"Fixed bug where the game list wasn't reloaded when using /reset." +
|
||||
"Fixed bug where the songNo showed in the list didn't match what should be sent.",
|
||||
},
|
||||
{
|
||||
Version: "1.1.0",
|
||||
Changelog: "Added sync endpoint, don't really trust it to 100%, would say beta. " +
|
||||
"Fixed bug with /next after /previous. Added /reset endpoint. " +
|
||||
"Added some info more to /info and /list.",
|
||||
},
|
||||
{
|
||||
Version: "1.0.0",
|
||||
Changelog: "Added swagger documentation. Created version 1.0.",
|
||||
},
|
||||
{
|
||||
Version: "0.5.5",
|
||||
Changelog: "Added increase played endpoint.",
|
||||
},
|
||||
},
|
||||
}
|
||||
_ = json.NewEncoder(w).Encode(data)
|
||||
|
||||
} else if r.URL.Path == "/doc" {
|
||||
http.ServeFile(w, r, "./doc/swagger.yaml")
|
||||
|
||||
} else if r.URL.Path == "/" {
|
||||
rows, dbErr := dbPool.Query(context.Background(), "select game_name from game")
|
||||
if dbErr != nil {
|
||||
_, _ = fmt.Fprintf(os.Stderr, "QueryRow failed: %v\n", dbErr)
|
||||
os.Exit(1)
|
||||
}
|
||||
for rows.Next() {
|
||||
var gameName string
|
||||
dbErr = rows.Scan(&gameName)
|
||||
if dbErr != nil {
|
||||
_, _ = fmt.Fprintf(os.Stderr, "QueryRow failed: %v\n", dbErr)
|
||||
}
|
||||
_, _ = fmt.Fprintf(w, "%v\n", gameName)
|
||||
}
|
||||
|
||||
_, err := fmt.Fprint(w, "Hello, World!!")
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
@@ -36,4 +159,4 @@ func indexHandler(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
BIN
songs/01. Opening.mp3
Executable file
BIN
songs/01. Opening.mp3
Executable file
Binary file not shown.
BIN
songs/01. Title.mp3
Executable file
BIN
songs/01. Title.mp3
Executable file
Binary file not shown.
153
syncFacade.go
Normal file
153
syncFacade.go
Normal file
@@ -0,0 +1,153 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func syncHandler(w http.ResponseWriter, r *http.Request) {
|
||||
if r.URL.Path == "/sync" {
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
syncGames()
|
||||
_, err := fmt.Fprint(w, "Games are synced")
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
}
|
||||
|
||||
} else if r.URL.Path == "/sync/reset" {
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
resetDB()
|
||||
_, err := fmt.Fprint(w, "Games and songs are deleted from the database")
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func syncGames() {
|
||||
host := os.Getenv("DB_HOST")
|
||||
var dir string
|
||||
if host != "" {
|
||||
dir = "/sorted/"
|
||||
} else {
|
||||
dir = "/Users/sebastian/Resilio Sync/Sorterat_test/"
|
||||
}
|
||||
fmt.Printf("dir: %s\n", dir)
|
||||
foldersToSkip := []string{".sync"}
|
||||
fmt.Println(foldersToSkip)
|
||||
setGameDeletionDate()
|
||||
|
||||
files, err := ioutil.ReadDir(dir)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
for _, file := range files {
|
||||
if file.IsDir() && !contains(foldersToSkip, file.Name()) {
|
||||
fmt.Println(file.Name())
|
||||
path := dir + file.Name() + "/"
|
||||
fmt.Println(path)
|
||||
|
||||
innerFiles, err := ioutil.ReadDir(path)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
id := -1
|
||||
for _, song := range innerFiles {
|
||||
id = getIdFromFile(song)
|
||||
if id != -1 {
|
||||
break
|
||||
}
|
||||
}
|
||||
if id == -1 {
|
||||
addNewGame(file.Name(), path)
|
||||
} else {
|
||||
checkIfChanged(id, file.Name(), path)
|
||||
checkSongs(path, id)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func getIdFromFile(file os.FileInfo) int {
|
||||
name := file.Name()
|
||||
if !file.IsDir() && strings.HasSuffix(name, ".id") {
|
||||
name = strings.Replace(name, ".id", "", 1)
|
||||
name = strings.Replace(name, ".", "", 1)
|
||||
i, _ := strconv.Atoi(name)
|
||||
return i
|
||||
}
|
||||
return -1
|
||||
}
|
||||
|
||||
func checkIfChanged(id int, name string, path string) {
|
||||
fmt.Printf("Id from file: %v\n", id)
|
||||
nameFromDb := getGameName(id)
|
||||
fmt.Printf("Name from file: %v\n", name)
|
||||
fmt.Printf("Name from DB: %v\n", nameFromDb)
|
||||
if nameFromDb == "" {
|
||||
fmt.Println("Not in db")
|
||||
insertGameWithExistingId(id, name, path)
|
||||
fmt.Println("Added to db")
|
||||
} else if name != nameFromDb {
|
||||
fmt.Println("Diff name")
|
||||
updateGameName(id, name, path)
|
||||
}
|
||||
removeDeletionDate(id)
|
||||
}
|
||||
|
||||
func addNewGame(name string, path string) {
|
||||
newId := getIdByGameName(name)
|
||||
if newId == -1 {
|
||||
newId = insertGame(name, path)
|
||||
}
|
||||
|
||||
fmt.Printf("newId = %v", newId)
|
||||
fileName := path + "/." + strconv.Itoa(newId) + ".id"
|
||||
fmt.Printf("fileName = %v", fileName)
|
||||
|
||||
err := ioutil.WriteFile(fileName, nil, 0644)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
checkSongs(path, newId)
|
||||
}
|
||||
|
||||
func checkSongs(gameDir string, gameId int) {
|
||||
songs := make([]SongData, 0)
|
||||
findSongsFromGame := findSongsFromGame(gameId)
|
||||
|
||||
files, err := ioutil.ReadDir(gameDir)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
for _, entry := range files {
|
||||
path := gameDir + entry.Name()
|
||||
songName := entry.Name()
|
||||
if !entry.IsDir() && !strings.HasSuffix(songName, ".id") {
|
||||
songs = append(songs, SongData{gameId: gameId, songName: songName, path: path})
|
||||
}
|
||||
}
|
||||
if len(songs) != len(findSongsFromGame) {
|
||||
clearSongs(gameId)
|
||||
for _, song := range songs {
|
||||
addSong(song)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func resetDB() {
|
||||
clearSongs(-1)
|
||||
clearGames()
|
||||
}
|
||||
|
||||
func contains(s []string, searchTerm string) bool {
|
||||
i := sort.SearchStrings(s, searchTerm)
|
||||
return i < len(s) && s[i] == searchTerm
|
||||
}
|
||||
Reference in New Issue
Block a user