init
This commit is contained in:
commit
c9450de603
18 changed files with 500 additions and 0 deletions
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
/client
|
||||
/server
|
||||
42
cmd/action/initcmd/process.go
Normal file
42
cmd/action/initcmd/process.go
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
package initcmd
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
|
||||
filestore "gitnet.fr/deblan/freetube-sync/store/file"
|
||||
"gitnet.fr/deblan/freetube-sync/web/client"
|
||||
"gitnet.fr/deblan/freetube-sync/web/route"
|
||||
)
|
||||
|
||||
func Process(name, route string, data any) bool {
|
||||
log.Print("Init of " + name)
|
||||
response, err := client.Init(route, data)
|
||||
res := true
|
||||
|
||||
if err != nil {
|
||||
log.Print("Error while initializing " + name + ": " + err.Error())
|
||||
res = false
|
||||
} else {
|
||||
if response.Code == 201 {
|
||||
log.Print(name + " initialized!")
|
||||
} else {
|
||||
log.Print("Error while initializing " + name + ": " + response.Message)
|
||||
res = false
|
||||
}
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
func Run() {
|
||||
a := Process("history", route.HistoryInit, filestore.LoadHistory())
|
||||
b := Process("playlists", route.PlaylistInit, filestore.LoadPlaylists())
|
||||
c := Process("profiles", route.ProfileInit, filestore.LoadProfiles())
|
||||
|
||||
if a && b && c {
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
os.Exit(1)
|
||||
}
|
||||
44
cmd/action/watchcmd/process.go
Normal file
44
cmd/action/watchcmd/process.go
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
package watchcmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/fsnotify/fsnotify"
|
||||
config "gitnet.fr/deblan/freetube-sync/config/client"
|
||||
)
|
||||
|
||||
func Run() {
|
||||
watcher, err := fsnotify.NewWatcher()
|
||||
|
||||
if err != nil {
|
||||
log.Print("Error while creating the watcher: " + err.Error())
|
||||
}
|
||||
|
||||
defer watcher.Close()
|
||||
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
case event, ok := <-watcher.Events:
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
if event.Has(fsnotify.Write) {
|
||||
switch event.Name {
|
||||
case config.GetConfig().Path + "/history.db":
|
||||
fmt.Printf("%+v\n", "update history")
|
||||
case config.GetConfig().Path + "/playlists.db":
|
||||
fmt.Printf("%+v\n", "update playlists")
|
||||
case config.GetConfig().Path + "/profiles.db":
|
||||
fmt.Printf("%+v\n", "update profiles")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
watcher.Add(config.GetConfig().Path)
|
||||
|
||||
<-make(chan struct{})
|
||||
}
|
||||
64
cmd/client/main.go
Normal file
64
cmd/client/main.go
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"gitnet.fr/deblan/freetube-sync/cmd/action/initcmd"
|
||||
"gitnet.fr/deblan/freetube-sync/cmd/action/watchcmd"
|
||||
config "gitnet.fr/deblan/freetube-sync/config/client"
|
||||
)
|
||||
|
||||
func main() {
|
||||
config.InitConfig()
|
||||
action := flag.Arg(0)
|
||||
|
||||
switch action {
|
||||
case "init":
|
||||
initcmd.Run()
|
||||
case "watch":
|
||||
watchcmd.Run()
|
||||
default:
|
||||
fmt.Print("You must pass a sub-command: init, watch")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// lines := file.GetLines("/home/simon/.config/FreeTube/history.db")
|
||||
// collection := []model.Video{}
|
||||
//
|
||||
// for _, line := range lines {
|
||||
// var item model.Video
|
||||
// json.Unmarshal([]byte(line), &item)
|
||||
//
|
||||
// collection = append(collection, item)
|
||||
// }
|
||||
//
|
||||
// data, err := json.Marshal(collection)
|
||||
//
|
||||
// if err != nil {
|
||||
// panic(err)
|
||||
// }
|
||||
//
|
||||
// req, err := http.NewRequest("POST", "http://localhost:1323/history/push", bytes.NewBuffer(data))
|
||||
// req.Header.Set("X-Machine", "endurance")
|
||||
// req.Header.Set("Content-Type", "application/json")
|
||||
//
|
||||
// if err != nil {
|
||||
// panic(err)
|
||||
// }
|
||||
//
|
||||
// client := &http.Client{}
|
||||
// resp, err := client.Do(req)
|
||||
// if err != nil {
|
||||
// panic(err)
|
||||
// }
|
||||
// defer resp.Body.Close()
|
||||
// fmt.Println("response Status:", resp.Status)
|
||||
// fmt.Println("response Headers:", resp.Header)
|
||||
// body, _ := io.ReadAll(resp.Body)
|
||||
// fmt.Println("response Body:", string(body))
|
||||
//
|
||||
// fmt.Printf("%+v\n", data)
|
||||
// fmt.Printf("%+v\n", collection)
|
||||
}
|
||||
47
cmd/server/main.go
Normal file
47
cmd/server/main.go
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"github.com/labstack/echo/v4"
|
||||
"gitnet.fr/deblan/freetube-sync/model"
|
||||
"gitnet.fr/deblan/freetube-sync/web/route"
|
||||
)
|
||||
|
||||
func main() {
|
||||
e := echo.New()
|
||||
|
||||
e.POST(route.HistoryInit, func(c echo.Context) error {
|
||||
payload := []model.Video{}
|
||||
err := c.Bind(&payload)
|
||||
|
||||
if err != nil {
|
||||
return c.JSON(400, map[string]any{
|
||||
"code": 400,
|
||||
"message": err,
|
||||
})
|
||||
}
|
||||
|
||||
return c.JSON(201, map[string]any{
|
||||
"code": 201,
|
||||
"message": "ok",
|
||||
})
|
||||
})
|
||||
|
||||
e.POST(route.HistoryPush, func(c echo.Context) error {
|
||||
payload := []model.Video{}
|
||||
err := c.Bind(&payload)
|
||||
|
||||
if err != nil {
|
||||
return c.JSON(400, map[string]any{
|
||||
"code": 400,
|
||||
"message": err,
|
||||
})
|
||||
}
|
||||
|
||||
return c.JSON(201, map[string]any{
|
||||
"code": 201,
|
||||
"message": "ok",
|
||||
})
|
||||
})
|
||||
|
||||
e.Logger.Fatal(e.Start(":1323"))
|
||||
}
|
||||
40
config/client/config.go
Normal file
40
config/client/config.go
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
package client
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
Server string
|
||||
Path string
|
||||
Hostname string
|
||||
}
|
||||
|
||||
var config *Config
|
||||
|
||||
func GetConfig() *Config {
|
||||
if config == nil {
|
||||
config = &Config{}
|
||||
}
|
||||
|
||||
return config
|
||||
}
|
||||
|
||||
func (c *Config) Define(server, hostname, path string) {
|
||||
c.Server = strings.TrimRight(server, "/")
|
||||
c.Hostname = hostname
|
||||
c.Path = path
|
||||
}
|
||||
|
||||
func InitConfig() {
|
||||
defaultHostname, _ := os.Hostname()
|
||||
|
||||
path := flag.String("p", os.Getenv("HOME")+"/.config/FreeTube", "Path to FreeTube config directory")
|
||||
hostname := flag.String("h", defaultHostname, "Hostname")
|
||||
server := flag.String("s", "", "Server to sync")
|
||||
flag.Parse()
|
||||
|
||||
GetConfig().Define(*server, *hostname, *path)
|
||||
}
|
||||
13
file/reader.go
Normal file
13
file/reader.go
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
package file
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func GetLines(file string) []string {
|
||||
bytesRead, _ := ioutil.ReadFile(file)
|
||||
fileContent := string(bytesRead)
|
||||
|
||||
return strings.Split(fileContent, "\n")
|
||||
}
|
||||
17
go.mod
Normal file
17
go.mod
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
module gitnet.fr/deblan/freetube-sync
|
||||
|
||||
go 1.23.0
|
||||
|
||||
require (
|
||||
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
||||
github.com/labstack/echo/v4 v4.12.0 // indirect
|
||||
github.com/labstack/gommon v0.4.2 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
github.com/valyala/fasttemplate v1.2.2 // indirect
|
||||
golang.org/x/crypto v0.22.0 // indirect
|
||||
golang.org/x/net v0.24.0 // indirect
|
||||
golang.org/x/sys v0.19.0 // indirect
|
||||
golang.org/x/text v0.14.0 // indirect
|
||||
)
|
||||
25
go.sum
Normal file
25
go.sum
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
|
||||
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
|
||||
github.com/labstack/echo/v4 v4.12.0 h1:IKpw49IMryVB2p1a4dzwlhP1O2Tf2E0Ir/450lH+kI0=
|
||||
github.com/labstack/echo/v4 v4.12.0/go.mod h1:UP9Cr2DJXbOK3Kr9ONYzNowSh7HP0aG0ShAyycHSJvM=
|
||||
github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0=
|
||||
github.com/labstack/gommon v0.4.2/go.mod h1:QlUFxVM+SNXhDL/Z7YhocGIBYOiwB0mXm1+1bAPHPyU=
|
||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||
github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo=
|
||||
github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
|
||||
golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30=
|
||||
golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
|
||||
golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
|
||||
golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
|
||||
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
11
model/playlist.go
Normal file
11
model/playlist.go
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
package model
|
||||
|
||||
type Playlist struct {
|
||||
PlaylistName string `json:"playlistName"`
|
||||
Protected bool `json:"protected"`
|
||||
Description string `json:"description"`
|
||||
Videos []Video `json:"videos"`
|
||||
Id string `json:"_id"`
|
||||
CreatedAt uint64 `json:"createdAt"`
|
||||
LastUpdatedAt uint64 `json:"lastUpdatedAt"`
|
||||
}
|
||||
8
model/profile.go
Normal file
8
model/profile.go
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
package model
|
||||
|
||||
type Profile struct {
|
||||
Name string `json:"name"`
|
||||
BgColor string `json:"bgColor"`
|
||||
TextColor string `json:"textColor"`
|
||||
Subscriptions []Subscription `json:"subscriptions"`
|
||||
}
|
||||
7
model/subscription.go
Normal file
7
model/subscription.go
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
package model
|
||||
|
||||
type Subscription struct {
|
||||
Id string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Thumbnail string `json:"thumbnail"`
|
||||
}
|
||||
19
model/video.go
Normal file
19
model/video.go
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
package model
|
||||
|
||||
type Video struct {
|
||||
VideoId string `json:"videoId"`
|
||||
Title string `json:"title"`
|
||||
Author string `json:"author"`
|
||||
AuthorId string `json:"authorId"`
|
||||
Published uint64 `json:"published"`
|
||||
Description string `json:"description"`
|
||||
ViewCount uint64 `json:"viewCount"`
|
||||
LengthSeconds uint64 `json:"lengthSeconds"`
|
||||
WatchProgress uint64 `json:"watchProgress"`
|
||||
TimeWatched uint64 `json:"timeWatched"`
|
||||
LsLive bool `json:"isLive"`
|
||||
Type string `json:"type"`
|
||||
Id string `json:"_id"`
|
||||
LastViewedPlaylistType string `json:"lastViewedPlaylistType"`
|
||||
LastViewedPlaylistItemId string `json:"lastViewedPlaylistItemId"`
|
||||
}
|
||||
23
store/file/history.go
Normal file
23
store/file/history.go
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
package file
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
config "gitnet.fr/deblan/freetube-sync/config/client"
|
||||
"gitnet.fr/deblan/freetube-sync/file"
|
||||
"gitnet.fr/deblan/freetube-sync/model"
|
||||
)
|
||||
|
||||
func LoadHistory() []model.Video {
|
||||
lines := file.GetLines(config.GetConfig().Path + "/history.db")
|
||||
collection := []model.Video{}
|
||||
|
||||
for _, line := range lines {
|
||||
var item model.Video
|
||||
json.Unmarshal([]byte(line), &item)
|
||||
|
||||
collection = append(collection, item)
|
||||
}
|
||||
|
||||
return collection
|
||||
}
|
||||
27
store/file/playlist.go
Normal file
27
store/file/playlist.go
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
package file
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
config "gitnet.fr/deblan/freetube-sync/config/client"
|
||||
"gitnet.fr/deblan/freetube-sync/file"
|
||||
"gitnet.fr/deblan/freetube-sync/model"
|
||||
)
|
||||
|
||||
func LoadPlaylists() []model.Playlist {
|
||||
lines := file.GetLines(config.GetConfig().Path + "/playlists.db")
|
||||
collection := []model.Playlist{}
|
||||
added := make(map[string]bool)
|
||||
|
||||
for i := len(lines) - 1; i >= 0; i-- {
|
||||
var item model.Playlist
|
||||
json.Unmarshal([]byte(lines[i]), &item)
|
||||
|
||||
if !added[item.Id] {
|
||||
added[item.Id] = true
|
||||
collection = append(collection, item)
|
||||
}
|
||||
}
|
||||
|
||||
return collection
|
||||
}
|
||||
27
store/file/subscription.go
Normal file
27
store/file/subscription.go
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
package file
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
config "gitnet.fr/deblan/freetube-sync/config/client"
|
||||
"gitnet.fr/deblan/freetube-sync/file"
|
||||
"gitnet.fr/deblan/freetube-sync/model"
|
||||
)
|
||||
|
||||
func LoadProfiles() []model.Profile {
|
||||
lines := file.GetLines(config.GetConfig().Path + "/profiles.db")
|
||||
collection := []model.Profile{}
|
||||
added := make(map[string]bool)
|
||||
|
||||
for i := len(lines) - 1; i >= 0; i-- {
|
||||
var item model.Profile
|
||||
json.Unmarshal([]byte(lines[i]), &item)
|
||||
|
||||
if !added[item.Name] {
|
||||
added[item.Name] = true
|
||||
collection = append(collection, item)
|
||||
}
|
||||
}
|
||||
|
||||
return collection
|
||||
}
|
||||
69
web/client/client.go
Normal file
69
web/client/client.go
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
package client
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
config "gitnet.fr/deblan/freetube-sync/config/client"
|
||||
)
|
||||
|
||||
type Data any
|
||||
|
||||
type PostResponse struct {
|
||||
Code int `json:"code"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
func Request(method, route string, data Data) ([]byte, error) {
|
||||
value, err := json.Marshal(data)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
url := fmt.Sprintf("%s%s", config.GetConfig().Server, route)
|
||||
request, _ := http.NewRequest(method, url, bytes.NewBuffer(value))
|
||||
request.Header.Set("X-Machine", config.GetConfig().Hostname)
|
||||
request.Header.Set("Content-Type", "application/json")
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
client := &http.Client{}
|
||||
response, err := client.Do(request)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
defer response.Body.Close()
|
||||
|
||||
body, err := io.ReadAll(response.Body)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return body, nil
|
||||
}
|
||||
|
||||
func Post(route string, data Data) ([]byte, error) {
|
||||
return Request("POST", route, data)
|
||||
}
|
||||
|
||||
func Get(route string, data Data) ([]byte, error) {
|
||||
return Request("POST", route, data)
|
||||
}
|
||||
|
||||
func Init(route string, data Data) (PostResponse, error) {
|
||||
var value PostResponse
|
||||
|
||||
body, err := Post(route, data)
|
||||
json.Unmarshal(body, &value)
|
||||
|
||||
return value, err
|
||||
}
|
||||
15
web/route/route.go
Normal file
15
web/route/route.go
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
package route
|
||||
|
||||
const (
|
||||
HistoryInit = "/history/init"
|
||||
HistoryPush = "/history/push"
|
||||
HistoryPull = "/history/pull"
|
||||
|
||||
ProfileInit = "/profile/init"
|
||||
ProfilePush = "/profile/push"
|
||||
ProfilePull = "/profile/pull"
|
||||
|
||||
PlaylistInit = "/playlist/init"
|
||||
PlaylistPush = "/playlist/push"
|
||||
PlaylistPull = "/playlist/pull"
|
||||
)
|
||||
Loading…
Add table
Add a link
Reference in a new issue