diff --git a/internal/pkg/api/api.go b/internal/pkg/api/api.go
index 6039f71..d23f45c 100644
--- a/internal/pkg/api/api.go
+++ b/internal/pkg/api/api.go
@@ -8,6 +8,7 @@ import (
"msw-open-music/internal/pkg/database"
"net/http"
"os"
+ "strconv"
)
type API struct {
@@ -36,23 +37,63 @@ type ResetRequest struct {
type SearchFilesRequest struct {
Filename string `json:"filename"`
Limit int64 `json:"limit"`
- Offset int64 `json:"offest"`
+ Offset int64 `json:"offset"`
}
type SearchFoldersRequest struct {
Foldername string `json:"foldername"`
Limit int64 `json:"limit"`
- Offset int64 `json:"offest"`
+ Offset int64 `json:"offset"`
}
-type SearchFilesRespond struct {
+type SearchFilesResponse struct {
Files []database.File `json:"files"`
}
-type SearchFoldersRespond struct {
+type SearchFoldersResponse struct {
Folders []database.Folder `json:"folders"`
}
+type GetFilesInFolderRequest struct {
+ Folder_id int64 `json:"folder_id"`
+ Limit int64 `json:"limit"`
+ Offset int64 `json:"offset"`
+}
+
+type GetFilesInFolderResponse struct {
+ Files *[]database.File `json:"files"`
+}
+
+func (api *API) HandleGetFilesInFolder(w http.ResponseWriter, r *http.Request) {
+ getFilesInFolderRequest := &GetFilesInFolderRequest{
+ Folder_id: -1,
+ }
+
+ err := json.NewDecoder(r.Body).Decode(getFilesInFolderRequest)
+ if err != nil {
+ api.HandleError(w, r, err)
+ return
+ }
+
+ // check empyt
+ if getFilesInFolderRequest.Folder_id < 0 {
+ api.HandleErrorString(w, r, `"folder_id" can't be none or negative`)
+ return
+ }
+
+ files, err := api.Db.GetFilesInFolder(getFilesInFolderRequest.Folder_id, getFilesInFolderRequest.Limit, getFilesInFolderRequest.Offset)
+ if err != nil {
+ api.HandleError(w, r, err)
+ return
+ }
+
+ getFilesInFolderResponse := &GetFilesInFolderResponse{
+ Files: &files,
+ }
+
+ json.NewEncoder(w).Encode(getFilesInFolderResponse)
+}
+
func (api *API) CheckToken(token string) (error) {
if token != api.token {
return errors.New("token not matched")
@@ -182,15 +223,15 @@ func (api *API) HandleSearchFiles(w http.ResponseWriter, r *http.Request) {
return
}
- searchFilesRespond := &SearchFilesRespond{}
+ searchFilesResponse := &SearchFilesResponse{}
- searchFilesRespond.Files, err = api.Db.SearchFiles(searchFilesRequest.Filename, searchFilesRequest.Limit, searchFilesRequest.Offset)
+ searchFilesResponse.Files, err = api.Db.SearchFiles(searchFilesRequest.Filename, searchFilesRequest.Limit, searchFilesRequest.Offset)
if err != nil {
api.HandleError(w, r, err)
return
}
- json.NewEncoder(w).Encode(searchFilesRespond)
+ json.NewEncoder(w).Encode(searchFilesResponse)
}
func (api *API) HandleSearchFolders(w http.ResponseWriter, r *http.Request) {
@@ -211,21 +252,48 @@ func (api *API) HandleSearchFolders(w http.ResponseWriter, r *http.Request) {
return
}
- searchFoldersRespond := &SearchFoldersRespond{}
+ searchFoldersResponse := &SearchFoldersResponse{}
- searchFoldersRespond.Folders, err = api.Db.SearchFolders(searchFoldersRequest.Foldername, searchFoldersRequest.Limit, searchFoldersRequest.Offset)
+ searchFoldersResponse.Folders, err = api.Db.SearchFolders(searchFoldersRequest.Foldername, searchFoldersRequest.Limit, searchFoldersRequest.Offset)
if err != nil {
api.HandleError(w, r, err)
return
}
- json.NewEncoder(w).Encode(searchFoldersRespond)
+ json.NewEncoder(w).Encode(searchFoldersResponse)
}
type GetFileRequest struct {
ID int64 `json:"id"`
}
+func (api *API) HandleGetFileDirect(w http.ResponseWriter, r *http.Request) {
+ q := r.URL.Query()
+ ids := q["id"]
+ if len(ids) == 0 {
+ api.HandleErrorString(w, r, `parameter "id" can't be empty`)
+ return
+ }
+ id, err := strconv.Atoi(ids[0])
+ if err != nil {
+ api.HandleErrorString(w, r, `parameter "id" should be an integer`)
+ return
+ }
+ file, err := api.Db.GetFile(int64(id))
+ if err != nil {
+ api.HandleError(w, r, err)
+ return
+ }
+
+ path, err := file.Path()
+ if err != nil {
+ api.HandleError(w, r, err)
+ return
+ }
+
+ http.ServeFile(w, r, path)
+}
+
func (api *API) HandleGetFile(w http.ResponseWriter, r *http.Request) {
getFilesRequest := &GetFileRequest{
ID: -1,
@@ -286,8 +354,10 @@ func NewAPI(dbName string, Addr string) (*API, error) {
// mount api
apiMux.HandleFunc("/hello", api.HandleOK)
apiMux.HandleFunc("/get_file", api.HandleGetFile)
+ apiMux.HandleFunc("/get_file_direct", api.HandleGetFileDirect)
apiMux.HandleFunc("/search_files", api.HandleSearchFiles)
apiMux.HandleFunc("/search_folders", api.HandleSearchFolders)
+ apiMux.HandleFunc("/get_files_in_folder", api.HandleGetFilesInFolder)
// below needs token
apiMux.HandleFunc("/walk", api.HandleWalk)
apiMux.HandleFunc("/reset", api.HandleReset)
diff --git a/internal/pkg/database/database.go b/internal/pkg/database/database.go
index 3b3387b..bee82dd 100644
--- a/internal/pkg/database/database.go
+++ b/internal/pkg/database/database.go
@@ -30,6 +30,7 @@ var dropFilesQuery = `DROP TABLE files;`
var dropFolderQuery = `DROP TABLE folders;`
var getFileQuery = `SELECT files.id, files.folder_id, files.filename, folders.foldername, files.filesize FROM files JOIN folders ON files.folder_id = folders.id WHERE files.id = ? LIMIT 1;`
var searchFoldersQuery = `SELECT id, folder, foldername FROM folders WHERE foldername LIKE ? LIMIT ? OFFSET ?;`
+var getFilesInFolderQuery = `SELECT id, filename, filesize FROM files WHERE folder_id = ? LIMIT ? OFFSET ?;`
type Database struct {
sqlConn *sql.DB
@@ -48,6 +49,7 @@ type Stmt struct {
dropFolder *sql.Stmt
getFile *sql.Stmt
searchFolders *sql.Stmt
+ getFilesInFolder *sql.Stmt
}
type File struct {
@@ -66,6 +68,27 @@ type Folder struct {
Foldername string `json:"foldername"`
}
+func (database *Database) GetFilesInFolder(folder_id int64, limit int64, offset int64) ([]File, error) {
+ rows, err := database.stmt.getFilesInFolder.Query(folder_id, limit, offset)
+ if err != nil {
+ return nil, err
+ }
+ defer rows.Close()
+ files := make([]File, 0)
+ for rows.Next() {
+ file := File{
+ Db: database,
+ Folder_id: folder_id,
+ }
+ err = rows.Scan(&file.ID, &file.Filename, &file.Filesize)
+ if err != nil {
+ return nil, err
+ }
+ files = append(files, file)
+ }
+ return files, nil
+}
+
func (database *Database) SearchFolders(foldername string, limit int64, offset int64) ([]Folder, error) {
rows, err := database.stmt.searchFolders.Query("%"+foldername+"%", limit, offset)
if err != nil {
@@ -323,6 +346,12 @@ func NewPreparedStatement(sqlConn *sql.DB) (*Stmt, error) {
return nil, err
}
+ // init getFilesInFolder stmt
+ stmt.getFilesInFolder, err = sqlConn.Prepare(getFilesInFolderQuery)
+ if err != nil {
+ return nil, err
+ }
+
return stmt, err
}
diff --git a/web/index.html b/web/index.html
index 69dc96d..72366f8 100644
--- a/web/index.html
+++ b/web/index.html
@@ -6,7 +6,7 @@
-
+
diff --git a/web/index.js b/web/index.js
index 4168cc0..ba0d594 100644
--- a/web/index.js
+++ b/web/index.js
@@ -1,30 +1,220 @@
const app = Vue.createApp({
data() {
return {
- search_filenames: '',
- download_total: 0,
- download_loaded: 0,
}
},
+})
+
+app.component('root-component', {
+ data() {
+ return {
+ playing_audio_file: {},
+ }
+ },
+ template: `
+
+
+
+
+`,
methods: {
+ play_audio(file) {
+ console.log(file)
+ this.playing_audio_file = file
+ },
+ },
+})
+
+app.component('component-search-folders', {
+ emits: ['play_audio'],
+ data() {
+ return {
+ search_foldernames: "",
+ folders: [],
+ offset: 0,
+ limit: 10,
+ folder_offset: 0,
+ folder_limit: 10,
+ files_in_folder: [],
+ playing_audio_file: {},
+ }
+ },
+ template: `
+
+
+
+
+{{ offset }}~{{ offset + folders.length }}
+
+
+
+
+
+ | ID |
+ Folder Name |
+ Action |
+
+
+
+
+ | {{ folder.id }} |
+ {{ folder.foldername }} |
+ |
+
+
+
+
+
+
+
+ | ID |
+ Filename |
+ Folder Name |
+ Size |
+ Action |
+
+
+
+
+
+
+
+
+`,
+ methods: {
+ get_files_in_folder(folder) {
+ axios.post('/api/v1/get_files_in_folder', {
+ folder_id: folder.id,
+ limit: this.folder_limit,
+ offset: this.folder_offset,
+ }).then((response) => {
+ this.files_in_folder = response.data.files
+ })
+ },
+ last_page() {
+ this.offset = this.offset - this.limit
+ if (this.offset < 0) {
+ this.offset = 0
+ return
+ }
+ this.search_folders()
+ },
+ next_page() {
+ this.offset = this.offset + this.limit
+ this.search_folders()
+ },
+ first_search_folders() {
+ this.offset = 0
+ this.search_folders()
+ },
+ search_folders() {
+ axios.post('/api/v1/search_folders', {
+ foldername: this.search_foldernames,
+ limit: this.limit,
+ offset: this.offset,
+ }).then((response) => {
+ this.folders = response.data.folders
+ })
+ },
+ },
+})
+
+app.component('component-update-database', {
+ data() {
+ return {
+ token: "",
+ root: "",
+ pattern: [".flac", ".mp3"],
+ pattern_tmp: "",
+ s: "",
+ }
+ },
+ template: `
+
+`,
+ methods: {
+ add_pattern() {
+ this.pattern.push(this.pattern_tmp)
+ this.pattern_tmp = ""
+ },
+ reset_database() {
+ axios.post('/api/v1/reset', {
+ token: this.token,
+ }).then((response) => {
+ this.s = response.data.status
+ }).catch((err) => {
+ this.s = err.response.data.status
+ })
+ },
+ update_database() {
+ this.s = "Updating..."
+ axios.post('/api/v1/walk', {
+ token: this.token,
+ root: this.root,
+ pattern: this.pattern,
+ }).then((response) => {
+ this.s = response.data.status
+ }).catch((err) => {
+ this.s = err.response.data.status
+ })
+ }
},
})
app.component('component-file', {
props: ['file'],
+ emits: ['play_audio'],
template: `
{{ file.id }} |
{{ file.filename }} |
+{{ file.foldername }} |
{{ computed_readable_size }} |
- |
+
+
+
+ |
`,
data() {
return {
download_loaded: 0,
+ disabled: false,
}
},
methods: {
+ emit_play_audio() {
+ this.$emit("play_audio", this.file)
+ },
download_file(file) {
+ this.disabled = true
axios({
url: '/api/v1/get_file',
method: 'POST',
@@ -33,7 +223,6 @@ app.component('component-file', {
id: file.id,
},
onDownloadProgress: ProgressEvent => {
- console.log(ProgressEvent.loaded)
this.download_loaded = ProgressEvent.loaded
}
}).then((response) => {
@@ -43,6 +232,8 @@ app.component('component-file', {
link.setAttribute('download', file.filename);
document.body.appendChild(link);
link.click();
+ this.download_loaded = 0
+ this.disabled = false
})
},
},
@@ -72,41 +263,86 @@ app.component('component-file', {
},
})
+app.component('component-audio-player', {
+ data() {
+ return {
+ }
+ },
+ props: ["file"],
+ template: `
+
+`,
+ computed: {
+ computed_playing_audio_file_url() {
+ return '/api/v1/get_file_direct?id=' + this.file.id
+ },
+ computed_show() {
+ return this.file.id ? true : false
+ },
+ },
+})
+
app.component('component-search-files', {
+ emits: ['play_audio'],
template: `
-
+
+
+{{ offset }}~{{ offset + files.length }}
+
| ID |
Filename |
+ Folder Name |
Size |
Action |
-
+
`,
data() {
return {
+ search_filenames: '',
files: [],
+ offset: 0,
+ limit: 10,
+ playing_audio_file: {},
}
},
methods: {
- search_files(app) {
- axios.post('http://localhost:8080/api/v1/search_files', {
- filename: app.search_filenames,
- limit: 10,
- offset: 0,
- }).then(function(response) {
- app.files = response.data.files
+ first_search_files() {
+ this.offset = 0
+ this.search_files()
+ },
+ search_files() {
+ axios.post('/api/v1/search_files', {
+ filename: this.search_filenames,
+ limit: this.limit,
+ offset: this.offset,
+ }).then((response) => {
+ this.files = response.data.files
})
},
+ last_page() {
+ this.offset = this.offset - this.limit
+ if (this.offset < 0) {
+ this.offset = 0
+ return
+ }
+ this.search_files()
+ },
+ next_page() {
+ this.offset = this.offset + this.limit
+ this.search_files()
+ },
},
})
app.mount('#app')