reupload after migration
This commit is contained in:
parent
9478f29c29
commit
5639101bac
|
@ -1,13 +1,15 @@
|
||||||
# ---> Go
|
# ---> Go
|
||||||
# If you prefer the allow list template instead of the deny list, see community template:
|
|
||||||
# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
|
|
||||||
#
|
|
||||||
# Binaries for programs and plugins
|
# Binaries for programs and plugins
|
||||||
*.exe
|
*.exe
|
||||||
*.exe~
|
*.exe~
|
||||||
*.dll
|
*.dll
|
||||||
*.so
|
*.so
|
||||||
*.dylib
|
*.dylib
|
||||||
|
Sonically
|
||||||
|
*Sonically
|
||||||
|
Sonically*
|
||||||
|
*Sonically.debug
|
||||||
|
Sonically.debug*
|
||||||
|
|
||||||
# Test binary, built with `go test -c`
|
# Test binary, built with `go test -c`
|
||||||
*.test
|
*.test
|
||||||
|
@ -18,6 +20,11 @@
|
||||||
# Dependency directories (remove the comment below to include it)
|
# Dependency directories (remove the comment below to include it)
|
||||||
# vendor/
|
# vendor/
|
||||||
|
|
||||||
# Go workspace file
|
# Personal data dir dynamicly created by App
|
||||||
go.work
|
.Sonically/**
|
||||||
|
.Sonically
|
||||||
|
|
||||||
|
# Build Environment Test and Backup Dir
|
||||||
|
build_environment/backup
|
||||||
|
build_environment/test
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
platform: linux/arm64
|
||||||
|
pipeline:
|
||||||
|
build:
|
||||||
|
image: golang:alpine
|
||||||
|
when:
|
||||||
|
event: tag
|
||||||
|
environment:
|
||||||
|
GOOS: linux
|
||||||
|
GOARCH: arm64
|
||||||
|
CGO_ENABLED: "1"
|
||||||
|
commands:
|
||||||
|
- "apk -U upgrade"
|
||||||
|
- "apk add \
|
||||||
|
protobuf-dev \
|
||||||
|
musl-dev \
|
||||||
|
protoc \
|
||||||
|
build-base \
|
||||||
|
pkgconfig \
|
||||||
|
gtk4.0-dev \
|
||||||
|
glib-dev \
|
||||||
|
gdk-pixbuf-dev \
|
||||||
|
opusfile-dev \
|
||||||
|
libnotify-dev \
|
||||||
|
portaudio-dev \
|
||||||
|
gobject-introspection-dev"
|
||||||
|
- "go build -tags netgo -o sonically_linux_musl-${CI_COMMIT_TAG##v}~aarch64"
|
||||||
|
- "sha512sum sonically_linux_musl-${CI_COMMIT_TAG##v}~aarch64 > sonically_linux_musl-${CI_COMMIT_TAG##v}~aarch64-sha512.sum"
|
||||||
|
- "sha256sum sonically_linux_musl-${CI_COMMIT_TAG##v}~aarch64 > sonically_linux_musl-${CI_COMMIT_TAG##v}~aarch64-sha256.sum"
|
||||||
|
- "md5sum sonically_linux_musl-${CI_COMMIT_TAG##v}~aarch64 > sonically_linux_musl-${CI_COMMIT_TAG##v}~aarch64-md5.sum"
|
||||||
|
publish:
|
||||||
|
image: plugins/gitea-release
|
||||||
|
settings:
|
||||||
|
base_url: https://git.itmodulo.eu
|
||||||
|
api_key:
|
||||||
|
from_secret: gitea_akey
|
||||||
|
files: sonically_linux_musl*
|
||||||
|
when:
|
||||||
|
event: tag
|
|
@ -0,0 +1,40 @@
|
||||||
|
platform: linux/amd64
|
||||||
|
pipeline:
|
||||||
|
build:
|
||||||
|
image: debian:bookworm
|
||||||
|
when:
|
||||||
|
event: tag
|
||||||
|
environment:
|
||||||
|
GOOS: linux
|
||||||
|
GOARCH: amd64
|
||||||
|
CGO_ENABLED: "1"
|
||||||
|
PATH: /usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin:/sbin:/usr/local/go/bin
|
||||||
|
commands:
|
||||||
|
- "apt-get update && apt-get upgrade -y && apt-get install wget git -y"
|
||||||
|
- "wget -q https://go.dev/dl/go1.18.3.linux-amd64.tar.gz && tar -zxf go1.18.3.linux-amd64.tar.gz -C /usr/local/ && go version"
|
||||||
|
- "apt-get install -y \
|
||||||
|
build-essential \
|
||||||
|
protobuf-compiler \
|
||||||
|
gcc \
|
||||||
|
upx \
|
||||||
|
pkg-config \
|
||||||
|
libgtk-4-dev \
|
||||||
|
libglib2.0-dev \
|
||||||
|
libopusfile-dev \
|
||||||
|
libnotify-dev \
|
||||||
|
libportaudio2 \
|
||||||
|
portaudio19-dev \
|
||||||
|
libgraphene-1.0-dev"
|
||||||
|
- "go build -tags netgo -o sonically_linux_glibc-${CI_COMMIT_TAG##v}~x86_64"
|
||||||
|
- "sha512sum sonically_linux_glibc-${CI_COMMIT_TAG##v}~x86_64 > sonically_linux_glibc-${CI_COMMIT_TAG##v}~x86_64-sha512.sum"
|
||||||
|
- "sha256sum sonically_linux_glibc-${CI_COMMIT_TAG##v}~x86_64 > sonically_linux_glibc-${CI_COMMIT_TAG##v}~x86_64-sha256.sum"
|
||||||
|
- "md5sum sonically_linux_glibc-${CI_COMMIT_TAG##v}~x86_64 > sonically_linux_glibc-${CI_COMMIT_TAG##v}~x86_64-md5.sum"
|
||||||
|
publish:
|
||||||
|
image: plugins/gitea-release
|
||||||
|
settings:
|
||||||
|
base_url: https://git.itmodulo.eu
|
||||||
|
api_key:
|
||||||
|
from_secret: gitea_akey
|
||||||
|
files: sonically_linux_glibc*
|
||||||
|
when:
|
||||||
|
event: tag
|
|
@ -0,0 +1,38 @@
|
||||||
|
platform: linux/amd64
|
||||||
|
pipeline:
|
||||||
|
build:
|
||||||
|
image: golang:alpine
|
||||||
|
when:
|
||||||
|
event: tag
|
||||||
|
environment:
|
||||||
|
GOOS: linux
|
||||||
|
GOARCH: amd64
|
||||||
|
CGO_ENABLED: "1"
|
||||||
|
commands:
|
||||||
|
- "apk -U upgrade"
|
||||||
|
- "apk add \
|
||||||
|
protobuf-dev \
|
||||||
|
musl-dev \
|
||||||
|
protoc \
|
||||||
|
build-base \
|
||||||
|
pkgconfig \
|
||||||
|
gtk4.0-dev \
|
||||||
|
glib-dev \
|
||||||
|
gdk-pixbuf-dev \
|
||||||
|
opusfile-dev \
|
||||||
|
libnotify-dev \
|
||||||
|
portaudio-dev \
|
||||||
|
gobject-introspection-dev"
|
||||||
|
- "go build -tags netgo -o sonically_linux_musl-${CI_COMMIT_TAG##v}~x86_64"
|
||||||
|
- "sha512sum sonically_linux_musl-${CI_COMMIT_TAG##v}~x86_64 > sonically_linux_musl-${CI_COMMIT_TAG##v}~x86_64-sha512.sum"
|
||||||
|
- "sha256sum sonically_linux_musl-${CI_COMMIT_TAG##v}~x86_64 > sonically_linux_musl-${CI_COMMIT_TAG##v}~x86_64-sha256.sum"
|
||||||
|
- "md5sum sonically_linux_musl-${CI_COMMIT_TAG##v}~x86_64 > sonically_linux_musl-${CI_COMMIT_TAG##v}~x86_64-md5.sum"
|
||||||
|
publish:
|
||||||
|
image: plugins/gitea-release
|
||||||
|
settings:
|
||||||
|
base_url: https://git.itmodulo.eu
|
||||||
|
api_key:
|
||||||
|
from_secret: gitea_akey
|
||||||
|
files: sonically_linux_musl*
|
||||||
|
when:
|
||||||
|
event: tag
|
|
@ -0,0 +1,40 @@
|
||||||
|
platform: linux/arm64
|
||||||
|
pipeline:
|
||||||
|
build:
|
||||||
|
image: debian:bookworm
|
||||||
|
when:
|
||||||
|
event: tag
|
||||||
|
environment:
|
||||||
|
GOOS: linux
|
||||||
|
GOARCH: arm64
|
||||||
|
CGO_ENABLED: "1"
|
||||||
|
PATH: /usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin:/sbin:/usr/local/go/bin
|
||||||
|
commands:
|
||||||
|
- "apt-get update && apt-get upgrade -y && apt-get install wget git -y"
|
||||||
|
- "wget -q https://go.dev/dl/go1.18.3.linux-arm64.tar.gz && tar -zxf go1.18.3.linux-arm64.tar.gz -C /usr/local/ && go version"
|
||||||
|
- "apt-get install -y \
|
||||||
|
build-essential \
|
||||||
|
protobuf-compiler \
|
||||||
|
gcc \
|
||||||
|
upx \
|
||||||
|
pkg-config \
|
||||||
|
libgtk-4-dev \
|
||||||
|
libglib2.0-dev \
|
||||||
|
libopusfile-dev \
|
||||||
|
libnotify-dev \
|
||||||
|
libportaudio2 \
|
||||||
|
portaudio19-dev \
|
||||||
|
libgraphene-1.0-dev"
|
||||||
|
- "go build -tags netgo -o sonically_linux_glibc-${CI_COMMIT_TAG##v}~aarch64"
|
||||||
|
- "sha512sum sonically_linux_glibc-${CI_COMMIT_TAG##v}~aarch64 > sonically_linux_glibc-${CI_COMMIT_TAG##v}~aarch64-sha512.sum"
|
||||||
|
- "sha256sum sonically_linux_glibc-${CI_COMMIT_TAG##v}~aarch64 > sonically_linux_glibc-${CI_COMMIT_TAG##v}~aarch64-sha256.sum"
|
||||||
|
- "md5sum sonically_linux_glibc-${CI_COMMIT_TAG##v}~aarch64 > sonically_linux_glibc-${CI_COMMIT_TAG##v}~aarch64-md5.sum"
|
||||||
|
publish:
|
||||||
|
image: plugins/gitea-release
|
||||||
|
settings:
|
||||||
|
base_url: https://git.itmodulo.eu
|
||||||
|
api_key:
|
||||||
|
from_secret: gitea_akey
|
||||||
|
files: sonically_linux_glibc*
|
||||||
|
when:
|
||||||
|
event: tag
|
30
README.md
30
README.md
|
@ -1,3 +1,29 @@
|
||||||
# Sonically
|
![Sonically logo](https://static.itmodulo.eu/files/l/sonically.svg)
|
||||||
|
|
||||||
GUI native Subsonic API client optimized for mobile outdoor usage on *nix platforms like pmOS, Mobian.
|
# Sonically [BETA]
|
||||||
|
|
||||||
|
## Status
|
||||||
|
***!!! SUSPENDED !!!***
|
||||||
|
|
||||||
|
I was forced to go back to Android, Sonically is not priority there.
|
||||||
|
I'm looking forwared to switch to pmOS again, I'll probably do so in the mid of 2023 and development will restart ;)
|
||||||
|
|
||||||
|
## Info
|
||||||
|
1. **Priority**: GTK4 is cool but not ideal, especially in go. I'll switch to something else. Probably native (for go) Fyne UI Toolkit. This should unlock seeking.
|
||||||
|
2. **Priority**: Clean code
|
||||||
|
3. Proxy settings, Lyrics self-fetching will be removed. This is non-goal now. You self-host you trust your server, you trust this app as it **should** connect only to this server. If you want more goodies, use advanced server implementation.
|
||||||
|
4. Since upcoming fix [*read important below]* I'll start using branches, at least 2, master and develop.
|
||||||
|
5. Packaging probably own repo.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
I lost somwhere working all binaries from 2022. Forgive me that.
|
||||||
|
Please compile yourself for now.
|
||||||
|
|
||||||
|
**Important**. First commit on this repo probably won't compile as it depends on LyricsApiGoExtneded that I don't longer care, also fscache may be broken. I'll try to fix that in the meantime.
|
||||||
|
|
||||||
|
**Hint** You may look into APKBUILD if you use pmOS, it should contain reciepe for building apkand
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
Ensure you have keyring named login. If no create it using e.g. seahorse.
|
||||||
|
|
||||||
|
Recommended settings are opus 128, buffer 4096 (probably only working, as some values may be hardcoded)
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
![Sonically logo](https://static.itmodulo.eu/files/l/sonically.svg)
|
||||||
|
|
||||||
|
# Sonically [BETA]
|
||||||
|
|
||||||
|
## Status
|
||||||
|
***!!! SUSPENDED !!!***
|
||||||
|
|
||||||
|
I was forced to go back to Android, Sonically is not priority there.
|
||||||
|
I'm looking forwared to switch to pmOS again, I'll probably do so in the mid of 2023 and development will restart ;)
|
||||||
|
|
||||||
|
## Info
|
||||||
|
1. **Priority**: GTK4 is cool but not ideal, especially in go. I'll switch to something else. Probably native (for go) Fyne UI Toolkit. This should unlock seeking.
|
||||||
|
2. **Priority**: Clean code
|
||||||
|
3. Proxy settings, Lyrics self-fetching will be removed. This is non-goal now. You self-host you trust your server, you trust this app as it **should** connect only to this server. If you want more goodies, use advanced server implementation.
|
||||||
|
4. Since upcoming fix [*read important below]* I'll start using branches, at least 2, master and develop.
|
||||||
|
5. Packaging probably own repo.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
I lost somwhere working all binaries from 2022. Forgive me that.
|
||||||
|
Please compile yourself for now.
|
||||||
|
|
||||||
|
**Important**. First commit on this repo probably won't compile as it depends on LyricsApiGoExtneded that I don't longer care, also fscache may be broken. I'll try to fix that in the meantime.
|
||||||
|
|
||||||
|
**Hint** You may look into APKBUILD if you use pmOS, it should contain reciepe for building apkand
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
Ensure you have keyring named login. If no create it using e.g. seahorse.
|
||||||
|
|
||||||
|
Recommended settings are opus 128, buffer 4096 (probably only working, as some values may be hardcoded)
|
|
@ -0,0 +1,381 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"runtime"
|
||||||
|
"time"
|
||||||
|
// "math"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"git.itmodulo.eu/LibrariesGo/lyrics-api-go-extended"
|
||||||
|
"github.com/TheCreeper/go-notify"
|
||||||
|
// "github.com/disintegration/imaging"
|
||||||
|
"github.com/diamondburned/gotk4/pkg/core/glib"
|
||||||
|
// "github.com/djherbis/fscache"
|
||||||
|
"github.com/gordonklaus/portaudio"
|
||||||
|
// "github.com/diamondburned/gotk4/pkg/gdk/v4"
|
||||||
|
// "github.com/delucks/go-subsonic"
|
||||||
|
"github.com/hraban/opus"
|
||||||
|
)
|
||||||
|
|
||||||
|
const rate float64 = 48000
|
||||||
|
const channels uint8 = 2
|
||||||
|
|
||||||
|
var PlayAudio bool = true // Used for play pause
|
||||||
|
var EndPlayer bool = false // Used for ending recursion
|
||||||
|
var StreamChanged bool = false
|
||||||
|
|
||||||
|
func PlayPort() {
|
||||||
|
|
||||||
|
nodeID := MainQueue[NowPlayingIndex]
|
||||||
|
songdata, _ := CurrentClient.GetSong(nodeID)
|
||||||
|
albmdata, _ := CurrentClient.GetAlbum(songdata.AlbumID)
|
||||||
|
|
||||||
|
// go glib.IdleAdd(FindSubtitles(songdata.Artist, songdata.Title))
|
||||||
|
TotalTime = songdata.Duration
|
||||||
|
|
||||||
|
// Async Gui update needed by GTK
|
||||||
|
|
||||||
|
var SetPlayingGuiData = func() {
|
||||||
|
MainArtistLabel.SetLabel(songdata.Artist)
|
||||||
|
MainSongTitleLabel.SetLabel(songdata.Title)
|
||||||
|
MainAlbumLabel.SetLabel(songdata.Album)
|
||||||
|
MainTotalTimeLabel.SetLabel(DurationToReadable(songdata.Duration))
|
||||||
|
|
||||||
|
DownloadCoverArtFullSize(albmdata.CoverArt)
|
||||||
|
var imgpath string = GetRunDir() + MainDir + DataDir + "/" + albmdata.CoverArt + ".jpg"
|
||||||
|
MainAlbumCover.SetFromFile(imgpath)
|
||||||
|
|
||||||
|
// path2 := os.UserHomeDir + MainDir + DataDir + "/" + albmdata.CoverArt + "-b.jpg"
|
||||||
|
|
||||||
|
// blu, err := os.Open(imgpath)
|
||||||
|
// if err != nil {
|
||||||
|
// LogOnError(err)
|
||||||
|
// }
|
||||||
|
// defer blu.Close()
|
||||||
|
|
||||||
|
// img, _, err := image.Decode(blu)
|
||||||
|
// if err != nil {
|
||||||
|
// LogOnError(err)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// dstImage := imaging.Blur(img, MainSettings.BlurRadius)
|
||||||
|
// err = imaging.Save(dstImage, path2)
|
||||||
|
// if err != nil {
|
||||||
|
// LogOnError(err)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// AD1
|
||||||
|
// MainAlbumCover.SetFromPixbuf(pixbuf)
|
||||||
|
|
||||||
|
MainPlayPauseButton.SetLabel("❚❚")
|
||||||
|
}
|
||||||
|
|
||||||
|
sig := make(chan os.Signal, 1)
|
||||||
|
signal.Notify(sig, os.Interrupt, os.Kill)
|
||||||
|
// param := map[string]string{
|
||||||
|
// "maxBitRate": strconv.Itoa(int(MainSettings.Bitrate)),
|
||||||
|
// "MainFormat": MainSettings.Codec,
|
||||||
|
// "transcodings": MainSettings.Codec,
|
||||||
|
// "estimateContentLength": "true",
|
||||||
|
// }
|
||||||
|
portaudio.Initialize()
|
||||||
|
defer portaudio.Terminate()
|
||||||
|
|
||||||
|
// create the cache, keys expire after 1 hour.
|
||||||
|
|
||||||
|
// var ReadMaster fscache.ReadAtCloser
|
||||||
|
// var WriteMaster io.WriteCloser
|
||||||
|
|
||||||
|
// IsCached := CurrentCache.Exists(nodeID)
|
||||||
|
|
||||||
|
ReadMaster, _, _ = CurrentCache.Get(nodeID)
|
||||||
|
|
||||||
|
// if !IsCached {
|
||||||
|
// reader, _ := CurrentClient.Stream(nodeID, param)
|
||||||
|
// go Backgroundread(reader, WriteMaster)
|
||||||
|
// } else {
|
||||||
|
// fmt.Println("Playing ", songdata.Artist, " - ", songdata.Title, " from cache")
|
||||||
|
// }
|
||||||
|
|
||||||
|
// Writing needs to be implemented in diffrenet place
|
||||||
|
// Here we use only reader
|
||||||
|
|
||||||
|
//AD2
|
||||||
|
|
||||||
|
OpusStr, err := opus.NewStream(ReadMaster)
|
||||||
|
chk(err)
|
||||||
|
result := <-AudioControlChan
|
||||||
|
fmt.Println("Result of channel is: ", result)
|
||||||
|
CurrentStream, _ := portaudio.OpenDefaultStream(0, 2, rate, portaudio.FramesPerBufferUnspecified, &Out)
|
||||||
|
// LogOnError(err)
|
||||||
|
EndPlayer = false
|
||||||
|
StreamChanged = true
|
||||||
|
PlayAudio = true
|
||||||
|
MainNotification := notify.NewNotification("Playing", songdata.Artist+": "+songdata.Title)
|
||||||
|
MainNotification.Timeout = 2000
|
||||||
|
if _, err := MainNotification.Show(); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
go glib.IdleAdd(SetPlayingGuiData)
|
||||||
|
MainAdjustment.Configure(float64(0), float64(0), float64(1), float64(1), float64(10), float64(0))
|
||||||
|
// chk(CurrentStream.Start())
|
||||||
|
|
||||||
|
for !EndPlayer {
|
||||||
|
if PlayAudio {
|
||||||
|
if StreamChanged {
|
||||||
|
CurrentStream.Start()
|
||||||
|
StreamChanged = false
|
||||||
|
}
|
||||||
|
_, err := OpusStr.Read(Out)
|
||||||
|
DrawnTime += float64(1920 / rate / 2)
|
||||||
|
glib.IdleAdd(UpdatePBar())
|
||||||
|
if (err == io.EOF) || (err == io.ErrUnexpectedEOF) || (TimeInPrecent >= 1.01) || (err == portaudio.StreamIsStopped) || (err == portaudio.TimedOut) {
|
||||||
|
fmt.Println("EOF", NowPlayingIndex, len(MainQueue)) // TODO Stram recover when unexpected
|
||||||
|
err = CurrentStream.Stop()
|
||||||
|
glib.IdleAdd(SetZeroTime())
|
||||||
|
defer CurrentStream.Close()
|
||||||
|
Out = make([]int16, 1920)
|
||||||
|
notify.CloseNotification(MainNotification.ReplacesID)
|
||||||
|
if NowPlayingIndex+1 < len(MainQueue) {
|
||||||
|
fmt.Println("Playing next song")
|
||||||
|
NowPlayingIndex++
|
||||||
|
go StartCaching(MainQueue[NowPlayingIndex])
|
||||||
|
if NowPlayingIndex+1 <= len(MainQueue)-1 {
|
||||||
|
// fmt.Println(songdata.Title, " +1 needs to be downloaded")
|
||||||
|
go CachingWaiter(MainQueue[NowPlayingIndex], MainQueue[NowPlayingIndex+1], 5)
|
||||||
|
}
|
||||||
|
if NowPlayingIndex+2 <= len(MainQueue)-1 {
|
||||||
|
// fmt.Println(songdata.Title, " +2 needs to be downloaded")
|
||||||
|
go CachingWaiter(MainQueue[NowPlayingIndex+1], MainQueue[NowPlayingIndex+2], 5)
|
||||||
|
}
|
||||||
|
go PlayPort() // Run new gouroutine to let func complete
|
||||||
|
EndPlayer = true
|
||||||
|
break
|
||||||
|
} else if (NowPlayingIndex + 1) == len(MainQueue) {
|
||||||
|
fmt.Println("End playing")
|
||||||
|
CurrentStream.Close()
|
||||||
|
EndPlayer = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// if (err != portaudio.OutputUnderflowed && err != nil) {
|
||||||
|
// fmt.Println(err)
|
||||||
|
// }
|
||||||
|
err := CurrentStream.Write()
|
||||||
|
if err != nil {
|
||||||
|
// LogOnError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
StreamChanged = true
|
||||||
|
CurrentStream.Stop()
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-sig:
|
||||||
|
CurrentStream.Close()
|
||||||
|
EndPlayer = true
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
PlayAudio = false
|
||||||
|
glib.IdleAdd(SetZeroTime())
|
||||||
|
fmt.Println("End playport")
|
||||||
|
AudioControlChan <- true
|
||||||
|
}
|
||||||
|
func Backgroundread(reader io.Reader, w io.WriteCloser, id string) {
|
||||||
|
fmt.Println("DEBUG: backgroundread ", id)
|
||||||
|
DownloadSyncGroup.Add(1)
|
||||||
|
readBuffer := make([]byte, MainSettings.DownladBufferSize)
|
||||||
|
for {
|
||||||
|
n, err := reader.Read(readBuffer)
|
||||||
|
if err != nil {
|
||||||
|
if err == io.EOF || err == io.ErrUnexpectedEOF {
|
||||||
|
w.Write(readBuffer[:n])
|
||||||
|
break
|
||||||
|
} else {
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
w.Write(readBuffer[:n])
|
||||||
|
}
|
||||||
|
w.Close()
|
||||||
|
runtime.GC()
|
||||||
|
DownloadSyncGroup.Done()
|
||||||
|
fmt.Println("Ended downloading ", id)
|
||||||
|
CachingMap[id] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
func CachingWaiter(idBlocking string, idNext string, waitTime int) {
|
||||||
|
isDownloaded, exists := CachingMap[idBlocking]
|
||||||
|
if !isDownloaded || !exists {
|
||||||
|
if waitTime == 0 {
|
||||||
|
waitTime = 3
|
||||||
|
}
|
||||||
|
time.Sleep(time.Duration(waitTime) * time.Second)
|
||||||
|
CachingWaiter(idBlocking, idNext, waitTime-1)
|
||||||
|
} else if isDownloaded && exists {
|
||||||
|
go StartCaching(idNext)
|
||||||
|
} else if !isDownloaded && exists {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func StartCaching(songID string) {
|
||||||
|
|
||||||
|
// songdata, _ := CurrentClient.GetSong(songID)
|
||||||
|
// albmdata, _ := CurrentClient.GetAlbum(songdata.AlbumID)
|
||||||
|
|
||||||
|
param := map[string]string{
|
||||||
|
"maxBitRate": strconv.Itoa(int(MainSettings.Bitrate)),
|
||||||
|
"MainFormat": MainSettings.Codec,
|
||||||
|
"transcodings": MainSettings.Codec,
|
||||||
|
"estimateContentLength": "true",
|
||||||
|
}
|
||||||
|
|
||||||
|
cacheExists := CurrentCache.Exists(songID)
|
||||||
|
_, WriteMaster, _ = CurrentCache.Get(songID)
|
||||||
|
|
||||||
|
if !cacheExists {
|
||||||
|
reader, _ := CurrentClient.Stream(songID, param)
|
||||||
|
// go DownloadCoverArtFullSize(albmdata.CoverArt)
|
||||||
|
Backgroundread(reader, WriteMaster, songID)
|
||||||
|
} else {
|
||||||
|
fmt.Println("Playing from cache")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GTK Async gui update
|
||||||
|
func UpdatePBar() func() {
|
||||||
|
var up = func() {
|
||||||
|
TimeInPrecent = float64(DrawnTime / float64(TotalTime))
|
||||||
|
MainTimeLeft.SetText(DurationToReadable(TotalTime - int(float64(DrawnTime))))
|
||||||
|
MainTimeDrawnLabel.SetText(DurationToReadable(int(float64(DrawnTime))))
|
||||||
|
MainAdjustment.SetValue(float64(DrawnTime / float64(TotalTime)))
|
||||||
|
}
|
||||||
|
return up
|
||||||
|
}
|
||||||
|
|
||||||
|
// GTK Async gui update
|
||||||
|
func SetZeroTime() func() {
|
||||||
|
TotalTime = 0
|
||||||
|
DrawnTime = 0
|
||||||
|
TimeInPrecent = 0.0
|
||||||
|
var up = func() {
|
||||||
|
MainPlayPauseButton.SetLabel("▶")
|
||||||
|
MainTimeLeft.SetText(":")
|
||||||
|
MainTimeDrawnLabel.SetText(":")
|
||||||
|
MainAdjustment.SetValue(TimeInPrecent)
|
||||||
|
MainTotalTimeLabel.SetLabel(":")
|
||||||
|
MainArtistLabel.SetLabel(":")
|
||||||
|
MainSongTitleLabel.SetLabel(":")
|
||||||
|
MainAlbumLabel.SetLabel(":")
|
||||||
|
|
||||||
|
}
|
||||||
|
return up
|
||||||
|
}
|
||||||
|
|
||||||
|
func chk(err error) {
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func DurationToReadable(dur int) string {
|
||||||
|
|
||||||
|
min := dur / 60
|
||||||
|
sec := dur % 60
|
||||||
|
|
||||||
|
if min >= 60 {
|
||||||
|
hr := dur / 3600
|
||||||
|
min = (dur - (hr * 3600)) / 60
|
||||||
|
sec = (dur - (hr * 3600) - (min * 60))
|
||||||
|
return (PrefixZeroLook(hr) + ":" + PrefixZeroLook(min) + ":" + PrefixZeroLook(sec))
|
||||||
|
}
|
||||||
|
return (PrefixZeroLook(min) + ":" + PrefixZeroLook(sec))
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func PrefixZeroLook(localInt int) string {
|
||||||
|
if localInt < 10 {
|
||||||
|
return ("0" + strconv.Itoa(localInt))
|
||||||
|
} else {
|
||||||
|
return strconv.Itoa(localInt)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func Exists(name string) bool {
|
||||||
|
_, err := os.Stat(name)
|
||||||
|
if err == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if errors.Is(err, os.ErrNotExist) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func FindSubtitles(artist string, song string) func() {
|
||||||
|
// TODO Repair
|
||||||
|
var fun = func() {
|
||||||
|
l := lyrics.New(*MetadataClient, lyrics.WithAllProviders())
|
||||||
|
lyric, err := l.Search(artist, song)
|
||||||
|
buf := MainSubtitlesTextView.Buffer()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Lyrics for %v-%v were not found", artist, song)
|
||||||
|
buf.SetText("Not found")
|
||||||
|
}
|
||||||
|
buf.SetText(lyric)
|
||||||
|
MainSubtitlesTextView.SetBuffer(buf)
|
||||||
|
}
|
||||||
|
return fun
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// AD1
|
||||||
|
// pixbuf2, err = gdk.PixbufNewFromFileAtSize(path2, w, h)
|
||||||
|
|
||||||
|
// gtk.LayoutNew()
|
||||||
|
|
||||||
|
// var css string = `
|
||||||
|
// GtkWindow {
|
||||||
|
// background-color: red;
|
||||||
|
// background-repeat: no-repeat;
|
||||||
|
// background-size: cover;
|
||||||
|
// background-position: center center;
|
||||||
|
// `
|
||||||
|
|
||||||
|
// var csscustom = `background-image: url("` + imgpath + `");
|
||||||
|
// }`
|
||||||
|
// var csscustom = `background-size: cover; background-color: red;
|
||||||
|
// }`
|
||||||
|
// fmt.Println(css + csscustom)
|
||||||
|
// prov, err := gtk.CssProviderNew()
|
||||||
|
// LogOnError(err)
|
||||||
|
// err = prov.LoadFromData(css + csscustom)
|
||||||
|
// LogOnError(err)
|
||||||
|
// scrn, err := gdk.ScreenGetDefault()
|
||||||
|
// LogOnError(err)
|
||||||
|
// gtk.AddProviderForScreen(scrn, prov, 1)
|
||||||
|
// MainWindow.ShowAll()
|
||||||
|
// LogFatalOnError(err)
|
||||||
|
|
||||||
|
// AD2
|
||||||
|
// path := os.UserHomeDir + MainDir + DataDir + "/" + nodeID.(string) + ".opus"
|
||||||
|
// var OpusStr *opus.Stream
|
||||||
|
// if !Exists(path) {
|
||||||
|
// f, err := os.OpenFile(path, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0600)
|
||||||
|
// chk(err)
|
||||||
|
// tee := io.TeeReader(reader, f)
|
||||||
|
// OpusStr, err = opus.NewStream(tee)
|
||||||
|
// } else {
|
|
@ -0,0 +1,24 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import "log"
|
||||||
|
|
||||||
|
func PanicOnError(err error) {
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func LogFatalOnError(err error) {
|
||||||
|
if err != nil {
|
||||||
|
// log.Fatalln(err)
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func LogOnError(err error) {
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
// panic(err)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
module git.itmodulo.eu/LinuxOnMobile/Sonically
|
||||||
|
|
||||||
|
go 1.18
|
||||||
|
|
||||||
|
require (
|
||||||
|
git.itmodulo.eu/LibrariesGo/lyrics-api-go-extended v0.1.45-0.20220502211740-bdde16173c5c
|
||||||
|
github.com/TheCreeper/go-notify v0.2.0
|
||||||
|
github.com/delucks/go-subsonic v0.0.0-20220623171311-8b0662f9f006
|
||||||
|
github.com/diamondburned/gotk4/pkg v0.0.0-20220529201008-66c7fe5d2b7c
|
||||||
|
github.com/djherbis/fscache v0.10.1
|
||||||
|
github.com/gordonklaus/portaudio v0.0.0-20220320131553-cc649ad523c1
|
||||||
|
github.com/hashicorp/go-retryablehttp v0.7.1
|
||||||
|
github.com/hraban/opus v0.0.0-20220302220929-eeacdbcb92d0
|
||||||
|
github.com/zalando/go-keyring v0.2.1
|
||||||
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/PuerkitoBio/goquery v1.8.0 // indirect
|
||||||
|
github.com/alessio/shellescape v1.4.1 // indirect
|
||||||
|
github.com/andybalholm/cascadia v1.3.1 // indirect
|
||||||
|
github.com/danieljoos/wincred v1.1.2 // indirect
|
||||||
|
github.com/godbus/dbus/v5 v5.1.0 // indirect
|
||||||
|
github.com/gosimple/slug v1.12.0 // indirect
|
||||||
|
github.com/gosimple/unidecode v1.0.1 // indirect
|
||||||
|
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
||||||
|
go4.org/unsafe/assume-no-moving-gc v0.0.0-20220617031537-928513b29760 // indirect
|
||||||
|
golang.org/x/net v0.0.0-20220630215102-69896b714898 // indirect
|
||||||
|
golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f // indirect
|
||||||
|
golang.org/x/sys v0.0.0-20220627191245-f75cf1eec38b // indirect
|
||||||
|
gopkg.in/djherbis/atime.v1 v1.0.0 // indirect
|
||||||
|
gopkg.in/djherbis/stream.v1 v1.3.1 // indirect
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect
|
||||||
|
)
|
|
@ -0,0 +1,87 @@
|
||||||
|
git.itmodulo.eu/LibrariesGo/lyrics-api-go-extended v0.1.45-0.20220502211740-bdde16173c5c h1:rMlCwAUtUci5aHwQ/vnN8x5k6veHEQ39qstNTBqDZOg=
|
||||||
|
git.itmodulo.eu/LibrariesGo/lyrics-api-go-extended v0.1.45-0.20220502211740-bdde16173c5c/go.mod h1:UvHix+BErO9l3dealuksQRc2RYWXSCqxUo3HurXPGvs=
|
||||||
|
github.com/PuerkitoBio/goquery v1.8.0 h1:PJTF7AmFCFKk1N6V6jmKfrNH9tV5pNE6lZMkG0gta/U=
|
||||||
|
github.com/PuerkitoBio/goquery v1.8.0/go.mod h1:ypIiRMtY7COPGk+I/YbZLbxsxn9g5ejnI2HSMtkjZvI=
|
||||||
|
github.com/TheCreeper/go-notify v0.2.0 h1:akzlSD8IWx+uOZqGNwS0FYsThYuw11JkXsYiQXY5kgo=
|
||||||
|
github.com/TheCreeper/go-notify v0.2.0/go.mod h1:paZnY8fMbaOyZLQWJitGWAMrO5ot3Ow7id47cyEL1KA=
|
||||||
|
github.com/alessio/shellescape v1.4.1 h1:V7yhSDDn8LP4lc4jS8pFkt0zCnzVJlG5JXy9BVKJUX0=
|
||||||
|
github.com/alessio/shellescape v1.4.1/go.mod h1:PZAiSCk0LJaZkiCSkPv8qIobYglO3FPpyFjDCtHLS30=
|
||||||
|
github.com/andybalholm/cascadia v1.3.1 h1:nhxRkql1kdYCc8Snf7D5/D3spOX+dBgjA6u8x004T2c=
|
||||||
|
github.com/andybalholm/cascadia v1.3.1/go.mod h1:R4bJ1UQfqADjvDa4P6HZHLh/3OxWWEqc0Sk8XGwHqvA=
|
||||||
|
github.com/danieljoos/wincred v1.1.0/go.mod h1:XYlo+eRTsVA9aHGp7NGjFkPla4m+DCL7hqDjlFjiygg=
|
||||||
|
github.com/danieljoos/wincred v1.1.2 h1:QLdCxFs1/Yl4zduvBdcHB8goaYk9RARS2SgLLRuAyr0=
|
||||||
|
github.com/danieljoos/wincred v1.1.2/go.mod h1:GijpziifJoIBfYh+S7BbkdUTU4LfM+QnGqR5Vl2tAx0=
|
||||||
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/delucks/go-subsonic v0.0.0-20220623171311-8b0662f9f006 h1:hFXcR3r0Vsnr+mMMwMrgm+mlAR18k/AuuEvNuMbNuSQ=
|
||||||
|
github.com/delucks/go-subsonic v0.0.0-20220623171311-8b0662f9f006/go.mod h1:vnbEuj6Z20PLcHB4rrLQAOXGMjtULfMGhRVSFPcSdUo=
|
||||||
|
github.com/diamondburned/gotk4/pkg v0.0.0-20220529201008-66c7fe5d2b7c h1:d09K0Y9HQ6/WLlFlH11dPt9TdDLIOxlztHaLUOpKa04=
|
||||||
|
github.com/diamondburned/gotk4/pkg v0.0.0-20220529201008-66c7fe5d2b7c/go.mod h1:rLH6FHos690jFgAM/GYEpMykuE/9NmN6zOvFlr8JTvE=
|
||||||
|
github.com/djherbis/fscache v0.10.1 h1:hDv+RGyvD+UDKyRYuLoVNbuRTnf2SrA2K3VyR1br9lk=
|
||||||
|
github.com/djherbis/fscache v0.10.1/go.mod h1:yyPYtkNnnPXsW+81lAcQS6yab3G2CRfnPLotBvtbf0c=
|
||||||
|
github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||||
|
github.com/godbus/dbus/v5 v5.0.6/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||||
|
github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
|
||||||
|
github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||||
|
github.com/gopherjs/gopherjs v1.17.2 h1:fQnZVsXk8uxXIStYb0N4bGk7jeyTalG/wsZjQ25dO0g=
|
||||||
|
github.com/gordonklaus/portaudio v0.0.0-20220320131553-cc649ad523c1 h1:FgUJ91JoMbS5qWXdIpnHta1hLtw1X8n2ek5JRED3R1I=
|
||||||
|
github.com/gordonklaus/portaudio v0.0.0-20220320131553-cc649ad523c1/go.mod h1:HfYnZi/ARQKG0dwH5HNDmPCHdLiFiBf+SI7DbhW7et4=
|
||||||
|
github.com/gosimple/slug v1.12.0 h1:xzuhj7G7cGtd34NXnW/yF0l+AGNfWqwgh/IXgFy7dnc=
|
||||||
|
github.com/gosimple/slug v1.12.0/go.mod h1:UiRaFH+GEilHstLUmcBgWcI42viBN7mAb818JrYOeFQ=
|
||||||
|
github.com/gosimple/unidecode v1.0.1 h1:hZzFTMMqSswvf0LBJZCZgThIZrpDHFXux9KeGmn6T/o=
|
||||||
|
github.com/gosimple/unidecode v1.0.1/go.mod h1:CP0Cr1Y1kogOtx0bJblKzsVWrqYaqfNOnHzpgWw4Awc=
|
||||||
|
github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM=
|
||||||
|
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
||||||
|
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
|
||||||
|
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
|
||||||
|
github.com/hashicorp/go-hclog v0.9.2 h1:CG6TE5H9/JXsFWJCfoIVpKFIkFe6ysEuHirp4DxCsHI=
|
||||||
|
github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
|
||||||
|
github.com/hashicorp/go-retryablehttp v0.7.1 h1:sUiuQAnLlbvmExtFQs72iFW/HXeUn8Z1aJLQ4LJJbTQ=
|
||||||
|
github.com/hashicorp/go-retryablehttp v0.7.1/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY=
|
||||||
|
github.com/hraban/opus v0.0.0-20220302220929-eeacdbcb92d0 h1:kWEAL53h9DdQ2Utz2vKhgLutpSS1L6WDB37xv1VMKwU=
|
||||||
|
github.com/hraban/opus v0.0.0-20220302220929-eeacdbcb92d0/go.mod h1:YQQXrWHN3JEvCtw5ImyTCcPeU/ZLo/YMA+TpB64XdrU=
|
||||||
|
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/smartystreets/assertions v1.13.0 h1:Dx1kYM01xsSqKPno3aqLnrwac2LetPvN23diwyr69Qs=
|
||||||
|
github.com/smartystreets/goconvey v1.7.2 h1:9RBaZCeXEQ3UselpuwUQHltGVXvdwm6cv1hgR6gDIPg=
|
||||||
|
github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4=
|
||||||
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||||
|
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||||
|
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||||
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
|
github.com/zalando/go-keyring v0.2.1 h1:MBRN/Z8H4U5wEKXiD67YbDAr5cj/DOStmSga70/2qKc=
|
||||||
|
github.com/zalando/go-keyring v0.2.1/go.mod h1:g63M2PPn0w5vjmEbwAX3ib5I+41zdm4esSETOn9Y6Dw=
|
||||||
|
go4.org/unsafe/assume-no-moving-gc v0.0.0-20220617031537-928513b29760 h1:FyBZqvoA/jbNzuAWLQE2kG820zMAkcilx6BMjGbL/E4=
|
||||||
|
go4.org/unsafe/assume-no-moving-gc v0.0.0-20220617031537-928513b29760/go.mod h1:FftLjUGFEDu5k8lt0ddY+HcrH/qU/0qk+H8j9/nTl3E=
|
||||||
|
golang.org/x/net v0.0.0-20210916014120-12bc252f5db8/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
|
golang.org/x/net v0.0.0-20220622184535-263ec571b305 h1:dAgbJ2SP4jD6XYfMNLVj0BF21jo2PjChrtGaAvF5M3I=
|
||||||
|
golang.org/x/net v0.0.0-20220622184535-263ec571b305/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
|
golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e h1:TsQ7F31D3bUCLeqPT0u+yjp1guoArKaNKmCr22PYgTQ=
|
||||||
|
golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
|
golang.org/x/net v0.0.0-20220630215102-69896b714898 h1:K7wO6V1IrczY9QOQ2WkVpw4JQSwCd52UsxVEirZUfiw=
|
||||||
|
golang.org/x/net v0.0.0-20220630215102-69896b714898/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
|
golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f h1:Ax0t5p6N38Ga0dThY21weqDEyz2oklo4IvDkpigvkD8=
|
||||||
|
golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20210819135213-f52c844e1c1c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664 h1:wEZYwx+kK+KlZ0hpvP2Ls1Xr4+RWnlzGFwPP0aiDjIU=
|
||||||
|
golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.0.0-20220627191245-f75cf1eec38b h1:2n253B2r0pYSmEV+UNCQoPfU/FiaizQEK5Gu4Bq4JE8=
|
||||||
|
golang.org/x/sys v0.0.0-20220627191245-f75cf1eec38b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
|
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/djherbis/atime.v1 v1.0.0 h1:eMRqB/JrLKocla2PBPKgQYg/p5UG4L6AUAs92aP7F60=
|
||||||
|
gopkg.in/djherbis/atime.v1 v1.0.0/go.mod h1:hQIUStKmJfvf7xdh/wtK84qe+DsTV5LnA9lzxxtPpJ8=
|
||||||
|
gopkg.in/djherbis/stream.v1 v1.3.1 h1:uGfmsOY1qqMjQQphhRBSGLyA9qumJ56exkRu9ASTjCw=
|
||||||
|
gopkg.in/djherbis/stream.v1 v1.3.1/go.mod h1:aEV8CBVRmSpLamVJfM903Npic1IKmb2qS30VAZ+sssg=
|
||||||
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
|
@ -0,0 +1,116 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
// "github.com/delucks/go-subsonic"
|
||||||
|
// "fmt"
|
||||||
|
"github.com/diamondburned/gotk4/pkg/core/glib"
|
||||||
|
)
|
||||||
|
|
||||||
|
func DownloadPlaylist() {
|
||||||
|
for child := PlayListListBox.FirstChild(); child != nil; {
|
||||||
|
PlayListListBox.Remove(child)
|
||||||
|
child = PlayListListBox.FirstChild()
|
||||||
|
}
|
||||||
|
param := map[string]string{}
|
||||||
|
playlists, err := CurrentClient.GetPlaylists(param)
|
||||||
|
LogOnError(err)
|
||||||
|
|
||||||
|
for i, item := range playlists {
|
||||||
|
playlist, _ := CurrentClient.GetPlaylist(item.ID)
|
||||||
|
PlaylistCached = append(PlaylistCached, item.ID)
|
||||||
|
|
||||||
|
pat := GetRunDir() + MainDir + InfDir + "/" + item.ID
|
||||||
|
SerializePlaylist(*playlist, pat)
|
||||||
|
|
||||||
|
var upstream = func() {
|
||||||
|
CreateImagelessPlaylistListBoxRow(PlayListListBox, playlist.Name, DurationToReadable(playlist.Duration), i, playlist.Entry)
|
||||||
|
}
|
||||||
|
glib.IdleAdd(upstream)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetPlaylist() {
|
||||||
|
|
||||||
|
for child := PlayListListBox.FirstChild(); child != nil; {
|
||||||
|
PlayListListBox.Remove(child)
|
||||||
|
child = PlayListListBox.FirstChild()
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, id := range PlaylistCached {
|
||||||
|
// fmt.Printf("{}",item)
|
||||||
|
|
||||||
|
item := DeSerializePlaylist(GetRunDir() + MainDir + InfDir + "/" + id)
|
||||||
|
|
||||||
|
var upstream = func() {
|
||||||
|
CreateImagelessPlaylistListBoxRow(PlayListListBox, item.Name, DurationToReadable(item.Duration), i, item.Entry)
|
||||||
|
}
|
||||||
|
glib.IdleAdd(upstream)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// func RefreshPlaylist() {
|
||||||
|
// for child := PlayListListBox.FirstChild(); child != nil; {
|
||||||
|
// PlayListListBox.Remove(child)
|
||||||
|
// child = PlayListListBox.FirstChild()
|
||||||
|
// }
|
||||||
|
// param := map[string]string{}
|
||||||
|
// playlists, err := CurrentClient.GetPlaylists(param)
|
||||||
|
// LogOnError(err)
|
||||||
|
// for i, item := range playlists {
|
||||||
|
// playlist, _ := CurrentClient.GetPlaylist(item.ID)
|
||||||
|
// CreateImagelessPlaylistListBoxRow(PlayListListBox, playlist.Name, DurationToReadable(playlist.Duration), i, playlist.Entry)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
func DownloadLib() {
|
||||||
|
param := map[string]string{}
|
||||||
|
artistID3, err := CurrentClient.GetArtists(param)
|
||||||
|
LogFatalOnError(err)
|
||||||
|
|
||||||
|
for _, indx := range artistID3.Index {
|
||||||
|
for j, artist := range indx.Artist {
|
||||||
|
art, _ := CurrentClient.GetArtist(artist.ID)
|
||||||
|
ArtistsCached = append(ArtistsCached, artist.ID)
|
||||||
|
|
||||||
|
SerializeArtist(*art, GetRunDir()+MainDir+InfDir+"/"+artist.ID)
|
||||||
|
|
||||||
|
var upstream = func() {
|
||||||
|
CreatelessListArtistBoxRow(QueueListBoxLibrary, art.Name, j, art.Album, art.ID)
|
||||||
|
}
|
||||||
|
glib.IdleAdd(upstream)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetLib() {
|
||||||
|
|
||||||
|
for child := QueueListBoxLibrary.FirstChild(); child != nil; {
|
||||||
|
QueueListBoxLibrary.Remove(child)
|
||||||
|
child = QueueListBoxLibrary.FirstChild()
|
||||||
|
}
|
||||||
|
|
||||||
|
for j, id := range ArtistsCached {
|
||||||
|
art := DeSerializeArtist(GetRunDir() + MainDir + InfDir + "/" + id)
|
||||||
|
var upstream = func() {
|
||||||
|
CreatelessListArtistBoxRow(QueueListBoxLibrary, art.Name, j, art.Album, art.ID)
|
||||||
|
}
|
||||||
|
glib.IdleAdd(upstream)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// func RefreshLib() {
|
||||||
|
// for child := QueueListBoxLibrary.FirstChild(); child != nil; {
|
||||||
|
// QueueListBoxLibrary.Remove(child)
|
||||||
|
// child = QueueListBoxLibrary.FirstChild()
|
||||||
|
// }
|
||||||
|
// param := map[string]string{}
|
||||||
|
// artistID3, err := CurrentClient.GetArtists(param)
|
||||||
|
// LogFatalOnError(err)
|
||||||
|
|
||||||
|
// for _, indx := range artistID3.Index {
|
||||||
|
// for j, artist := range indx.Artist {
|
||||||
|
// art, _ := CurrentClient.GetArtist(artist.ID)
|
||||||
|
// CreatelessListArtistBoxRow(QueueListBoxLibrary, art.Name, j, art.Album)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
|
@ -0,0 +1,39 @@
|
||||||
|
# Contributor: ITmodulo <hello@xmpp.itmodulo.eu>
|
||||||
|
# Maintainer: ITmodulo <hello@xmpp.itmodulo.eu>
|
||||||
|
pkgname="sonically"
|
||||||
|
reponame="Sonically"
|
||||||
|
organization="LinuxOnMobile"
|
||||||
|
githost="https://git.itmodulo.eu"
|
||||||
|
pkgver=0.4.1
|
||||||
|
pkgrel=0
|
||||||
|
pkgdesc="Subsonic API compatible streaming client made in GTK. It is suitable for desktop and mobile"
|
||||||
|
url="$githost/$organization/$reponame"
|
||||||
|
arch="x86_64 aarch64"
|
||||||
|
options="net !check" # for downloading Go modules, no checks available
|
||||||
|
license="GPL-3.0-or-later"
|
||||||
|
depends="portaudio opusfile libnotify glib gtk4.0 gobject-introspection"
|
||||||
|
makedepends="go portaudio-dev opusfile-dev libnotify-dev glib-dev gtk4.0-dev gobject-introspection-dev"
|
||||||
|
source="$reponame-$pkgver.tar.gz::$githost/$organization/$reponame/archive/$pkgver.tar.gz"
|
||||||
|
builddir="$srcdir/$pkgname-$pkgver"
|
||||||
|
|
||||||
|
prepare() {
|
||||||
|
default_prepare()
|
||||||
|
mkdir -p "$builddir"
|
||||||
|
}
|
||||||
|
|
||||||
|
build() {
|
||||||
|
# GOARCH should be set automaticaly by OS
|
||||||
|
cd "$srcdir"/"$pkgname"
|
||||||
|
CGO_ENABLED="1" GOOS="linux" go build -tags netgo -o "$builddir"/"$pkgname"
|
||||||
|
}
|
||||||
|
|
||||||
|
package() {
|
||||||
|
# Place built executable in PATH
|
||||||
|
install -Dm755 "$builddir"/"$pkgname" "$pkgdir"/usr/bin/"$pkgname"
|
||||||
|
# Copy .svg logo to system
|
||||||
|
install -Dm644 "$srcdir"/"$pkgname"/"$pkgname".svg "$pkgdir"/usr/share/pixmaps/"$pkgname".svg
|
||||||
|
# Copy .desktop file to to system
|
||||||
|
install -Dm755 "$srcdir"/"$pkgname"/"$pkgname".desktop "$pkgdir"/usr/share/applications/eu.itmodulo."$pkgname".desktop
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,89 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
// "image"
|
||||||
|
"image/jpeg"
|
||||||
|
// "bytes"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
// "github.com/esimov/stackblur-go"
|
||||||
|
// "github.com/diamondburned/gotk4/pkg/gtk/v4"
|
||||||
|
// "github.com/diamondburned/gotk4/pkg/gdkpixbuf/v2"
|
||||||
|
// "github.com/diamondburned/gotk4/pkg/cairo"
|
||||||
|
// "github.com/diamondburned/gotk4/pkg/gdk/v4"
|
||||||
|
// "github.com/diamondburned/gotkit/gtkutil/imgutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
func DownloadCoverArtFullSize(coverID string) {
|
||||||
|
|
||||||
|
path := GetRunDir() + MainDir + DataDir + "/" + coverID + ".jpg"
|
||||||
|
|
||||||
|
fil, err1 := os.OpenFile(path, os.O_APPEND|os.O_WRONLY, 0664)
|
||||||
|
defer fil.Close()
|
||||||
|
if err1 != nil {
|
||||||
|
//
|
||||||
|
param := map[string]string{"size": strconv.Itoa(int(MainSettings.Coversize))}
|
||||||
|
|
||||||
|
img, _ := CurrentClient.GetCoverArt(coverID, param)
|
||||||
|
fmt.Println("No such album downloaded: ", coverID)
|
||||||
|
f, err := os.Create(path)
|
||||||
|
// fb, err := os.Create(path2)
|
||||||
|
PanicOnError(err)
|
||||||
|
defer f.Close()
|
||||||
|
if err = jpeg.Encode(f, img, nil); err != nil {
|
||||||
|
log.Printf("failed to encode: %v", err)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// w, h := MainAlbumCover.GetSizeRequest()
|
||||||
|
|
||||||
|
// catFile, err := os.Open(path)
|
||||||
|
// if err != nil {
|
||||||
|
// panic(err)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// defer catFile.Close()
|
||||||
|
// _, _, err = image.Decode(catFile)
|
||||||
|
// if err != nil {
|
||||||
|
// panic(err)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// cat, err := jpeg.Decode(catFile)
|
||||||
|
// if err != nil {
|
||||||
|
// panic(err)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// fmt.Println(cat)
|
||||||
|
// sig := make(chan struct{})
|
||||||
|
// img2 := stackblur.Process(cat, uint32(w), uint32(h), uint32(30), sig)
|
||||||
|
// select {
|
||||||
|
|
||||||
|
// case <-sig:
|
||||||
|
// jpeg.Encode(fb, img2, nil)
|
||||||
|
// }
|
||||||
|
} else {
|
||||||
|
|
||||||
|
fmt.Println("Album present: ", coverID)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// func LoadImageToByteArray(path string) []byte {
|
||||||
|
|
||||||
|
// f, err := os.Open(path)
|
||||||
|
// if err != nil {
|
||||||
|
// LogOnError(err)
|
||||||
|
// }
|
||||||
|
// defer f.Close()
|
||||||
|
// buf := new(bytes.Buffer)
|
||||||
|
// img, err := jpeg.Decode(f)
|
||||||
|
// err = jpeg.Encode(buf,img,nil)
|
||||||
|
|
||||||
|
// if err != nil {
|
||||||
|
// LogOnError(err)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// return buf.Bytes()
|
||||||
|
// }
|
|
@ -0,0 +1,65 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
// "log"
|
||||||
|
"strconv"
|
||||||
|
// "fmt"
|
||||||
|
// "github.com/diamondburned/gotk4/pkg/core/glib"
|
||||||
|
// "github.com/diamondburned/gotk4/pkg/gdk/v4"
|
||||||
|
// "github.com/diamondburned/gotk4/pkg/gtk/v4"
|
||||||
|
// "github.com/diamondburned/gotkit/gtkutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
// var PlayNode string
|
||||||
|
// var pool *parallel = parallel.NewParallel()
|
||||||
|
|
||||||
|
func Search() {
|
||||||
|
|
||||||
|
for child := SearchListBoxResult.FirstChild(); child != nil; {
|
||||||
|
SearchListBoxResult.Remove(child)
|
||||||
|
child = SearchListBoxResult.FirstChild()
|
||||||
|
}
|
||||||
|
|
||||||
|
artistCount := 0
|
||||||
|
albumCount := 0
|
||||||
|
songCount := 0
|
||||||
|
|
||||||
|
if SearchTabSongCheckButton.Active() {
|
||||||
|
songCount = 20
|
||||||
|
}
|
||||||
|
if SearchTabAlbumCheckButton.Active() {
|
||||||
|
albumCount = 20
|
||||||
|
}
|
||||||
|
if SearchTabArtistCheckButton.Active() {
|
||||||
|
artistCount = 20
|
||||||
|
}
|
||||||
|
q := SearchTabSearchEntry.Text()
|
||||||
|
param := map[string]string{
|
||||||
|
"artistCount": strconv.Itoa(artistCount),
|
||||||
|
// "artistOffset": "0",
|
||||||
|
"albumCount": strconv.Itoa(albumCount),
|
||||||
|
// "albumOffset": "0",
|
||||||
|
"songCount": strconv.Itoa(songCount),
|
||||||
|
// "songOffset": "0",
|
||||||
|
// "musicFolderId": "",
|
||||||
|
}
|
||||||
|
if q != "" {
|
||||||
|
result, err := CurrentClient.Search3(q, param)
|
||||||
|
LogFatalOnError(err)
|
||||||
|
var j = 0
|
||||||
|
for _, item := range result.Song {
|
||||||
|
j++
|
||||||
|
CreateImagelessSongListBoxRow(SearchListBoxResult, item.Title, item.Artist, DurationToReadable(item.Duration), j, item.ID)
|
||||||
|
}
|
||||||
|
for _, item := range result.Album {
|
||||||
|
j++
|
||||||
|
album, _ := CurrentClient.GetAlbum(item.ID)
|
||||||
|
CreateImagelessAlbumListBoxRow(SearchListBoxResult, album.Name, album.Artist, DurationToReadable(album.Duration), j, album.Song)
|
||||||
|
}
|
||||||
|
for _, item := range result.Artist {
|
||||||
|
j++
|
||||||
|
artist, _ := CurrentClient.GetArtist(item.ID)
|
||||||
|
CreatelessListArtistBoxRow(SearchListBoxResult, artist.Name, j, artist.Album, item.ID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,351 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/gob"
|
||||||
|
"fmt"
|
||||||
|
"github.com/delucks/go-subsonic"
|
||||||
|
"github.com/diamondburned/gotk4/pkg/core/glib"
|
||||||
|
// "io"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
// "github.com/diamondburned/gotk4/pkg/gtk/v4"
|
||||||
|
"github.com/djherbis/fscache"
|
||||||
|
"github.com/zalando/go-keyring"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestConnButtonClicked() {
|
||||||
|
clientt := CreateUser(HttpSettings)
|
||||||
|
_, err := clientt.GetGenres()
|
||||||
|
if err != nil {
|
||||||
|
var up = func() bool {
|
||||||
|
SettingsTabUserStatusLabel.SetText("✗ ")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
glib.IdleAdd(up)
|
||||||
|
|
||||||
|
} else {
|
||||||
|
var up = func() bool {
|
||||||
|
SettingsTabUserStatusLabel.SetText("✓ ")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
glib.IdleAdd(up)
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func CreateUser(httpsettings *http.Client) subsonic.Client {
|
||||||
|
server := SettingsTabUserServerEntry.Text()
|
||||||
|
username := SettingsTabUserUsernameEntry.Text()
|
||||||
|
device := SettingsTabUserDeviceNameEntry.Text()
|
||||||
|
pass := SettingsTabUserPasswordEntry.Text()
|
||||||
|
|
||||||
|
clientt := subsonic.Client{
|
||||||
|
Client: httpsettings,
|
||||||
|
BaseUrl: server,
|
||||||
|
User: username,
|
||||||
|
ClientName: device,
|
||||||
|
}
|
||||||
|
clientt.Authenticate(pass)
|
||||||
|
|
||||||
|
return clientt
|
||||||
|
}
|
||||||
|
|
||||||
|
type User struct {
|
||||||
|
User string
|
||||||
|
Url string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Settings struct {
|
||||||
|
//User
|
||||||
|
Users [10]User
|
||||||
|
PlayerName string
|
||||||
|
|
||||||
|
//Local
|
||||||
|
DeleteOnClose bool
|
||||||
|
Coversize int16
|
||||||
|
BlurRadius int8
|
||||||
|
HideHeaderBar bool
|
||||||
|
|
||||||
|
//Transcoding
|
||||||
|
Codec string
|
||||||
|
Bitrate int16
|
||||||
|
DownladBufferSize int16
|
||||||
|
|
||||||
|
//Net
|
||||||
|
UseStreamingProxy bool
|
||||||
|
StreamingProxy string
|
||||||
|
AuthStreamingProxy bool
|
||||||
|
StreamingProxyUser string
|
||||||
|
|
||||||
|
// Metadata
|
||||||
|
MetadataType string
|
||||||
|
UseMetadataProxy bool
|
||||||
|
MetadataProxy string
|
||||||
|
AuthMetadataProxy bool
|
||||||
|
MetadataProxyUser string
|
||||||
|
}
|
||||||
|
|
||||||
|
// type SonicallyStream struct {
|
||||||
|
// ReadMaster fscache.ReadAtCloser
|
||||||
|
// WriteMaster io.WriteCloser
|
||||||
|
// }
|
||||||
|
|
||||||
|
func Serialize(obj Settings, path string) {
|
||||||
|
dataFile, err1 := os.OpenFile(path, os.O_APPEND|os.O_WRONLY, 0664)
|
||||||
|
defer dataFile.Close()
|
||||||
|
if err1 == nil {
|
||||||
|
os.Remove(path)
|
||||||
|
}
|
||||||
|
dataFile, _ = os.Create(path)
|
||||||
|
e := gob.NewEncoder(dataFile)
|
||||||
|
if err := e.Encode(obj); err != nil {
|
||||||
|
LogFatalOnError(err)
|
||||||
|
}
|
||||||
|
dataFile.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func DeSerialize(path string) Settings {
|
||||||
|
dataFile, err := os.Open(path)
|
||||||
|
if err != nil {
|
||||||
|
PanicOnError(err)
|
||||||
|
}
|
||||||
|
var sett Settings
|
||||||
|
dataDecoder := gob.NewDecoder(dataFile)
|
||||||
|
err = dataDecoder.Decode(&sett)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
PanicOnError(err)
|
||||||
|
}
|
||||||
|
dataFile.Close()
|
||||||
|
return sett
|
||||||
|
}
|
||||||
|
|
||||||
|
func SerializeArtist(obj subsonic.ArtistID3, path string) {
|
||||||
|
dataFile, err1 := os.OpenFile(path, os.O_APPEND|os.O_WRONLY, 0664)
|
||||||
|
defer dataFile.Close()
|
||||||
|
if err1 == nil {
|
||||||
|
os.Remove(path)
|
||||||
|
}
|
||||||
|
dataFile, _ = os.Create(path)
|
||||||
|
e := gob.NewEncoder(dataFile)
|
||||||
|
if err := e.Encode(obj); err != nil {
|
||||||
|
LogFatalOnError(err)
|
||||||
|
}
|
||||||
|
dataFile.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func DeSerializeArtist(path string) subsonic.ArtistID3 {
|
||||||
|
dataFile, err := os.Open(path)
|
||||||
|
if err != nil {
|
||||||
|
PanicOnError(err)
|
||||||
|
}
|
||||||
|
var sett subsonic.ArtistID3
|
||||||
|
dataDecoder := gob.NewDecoder(dataFile)
|
||||||
|
err = dataDecoder.Decode(&sett)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
PanicOnError(err)
|
||||||
|
}
|
||||||
|
dataFile.Close()
|
||||||
|
return sett
|
||||||
|
}
|
||||||
|
|
||||||
|
func SerializePlaylist(obj subsonic.Playlist, path string) {
|
||||||
|
dataFile, err1 := os.OpenFile(path, os.O_APPEND|os.O_WRONLY, 0664)
|
||||||
|
defer dataFile.Close()
|
||||||
|
if err1 == nil {
|
||||||
|
os.Remove(path)
|
||||||
|
}
|
||||||
|
dataFile, _ = os.Create(path)
|
||||||
|
e := gob.NewEncoder(dataFile)
|
||||||
|
if err := e.Encode(obj); err != nil {
|
||||||
|
LogFatalOnError(err)
|
||||||
|
}
|
||||||
|
dataFile.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
func DeSerializePlaylist(path string) subsonic.Playlist {
|
||||||
|
dataFile, err := os.Open(path)
|
||||||
|
if err != nil {
|
||||||
|
PanicOnError(err)
|
||||||
|
}
|
||||||
|
var sett subsonic.Playlist
|
||||||
|
dataDecoder := gob.NewDecoder(dataFile)
|
||||||
|
err = dataDecoder.Decode(&sett)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
PanicOnError(err)
|
||||||
|
}
|
||||||
|
dataFile.Close()
|
||||||
|
return sett
|
||||||
|
}
|
||||||
|
|
||||||
|
func Init() {
|
||||||
|
fil, err1 := os.OpenFile(settingsPath, os.O_APPEND|os.O_WRONLY, 0664)
|
||||||
|
defer fil.Close()
|
||||||
|
if err1 == nil {
|
||||||
|
MainSettings = DeSerialize(settingsPath)
|
||||||
|
UserList = MainSettings.Users
|
||||||
|
UserDeserialized = MainSettings.Users[0]
|
||||||
|
|
||||||
|
clientt := subsonic.Client{
|
||||||
|
Client: HttpSettings,
|
||||||
|
BaseUrl: UserDeserialized.Url,
|
||||||
|
User: UserDeserialized.User,
|
||||||
|
ClientName: MainSettings.PlayerName,
|
||||||
|
}
|
||||||
|
|
||||||
|
sec, err := keyring.Get(UserDeserialized.Url, UserDeserialized.User)
|
||||||
|
if err == nil {
|
||||||
|
clientt.Authenticate(sec)
|
||||||
|
CurrentClient = clientt
|
||||||
|
SettingsTabUserUsernameEntry.SetText(UserDeserialized.User)
|
||||||
|
SettingsTabUserPasswordEntry.SetText(sec)
|
||||||
|
SettingsTabUserDeviceNameEntry.SetText(MainSettings.PlayerName)
|
||||||
|
SettingsTabUserServerEntry.SetText(UserDeserialized.Url)
|
||||||
|
SettingsTabAppDeleteSwitch.SetActive(MainSettings.DeleteOnClose)
|
||||||
|
SettingsTabAppHideHeaderBarSwitch.SetActive(MainSettings.HideHeaderBar)
|
||||||
|
|
||||||
|
SettingsTabAppCoverSizeEntry.SetText(strconv.Itoa(int(MainSettings.Coversize)))
|
||||||
|
SettingsTabAppBlurRadiusEntry.SetText(strconv.Itoa(int(MainSettings.BlurRadius)))
|
||||||
|
SettingsTabAppBitrateEntry.SetText(strconv.Itoa(int(MainSettings.Bitrate)))
|
||||||
|
SettingsTabAppBufferSizeEntry.SetText(strconv.Itoa(int(MainSettings.DownladBufferSize)))
|
||||||
|
SettingsTabAppStreamingProxySwitch.SetActive(MainSettings.UseStreamingProxy)
|
||||||
|
if MainSettings.UseStreamingProxy == true {
|
||||||
|
SettingsTabAppStreamingProxyEntry.SetText(MainSettings.StreamingProxy)
|
||||||
|
|
||||||
|
// var temptrans *http.Transport
|
||||||
|
var protocol, parsed string
|
||||||
|
|
||||||
|
if strings.Contains(MainSettings.StreamingProxy, "https") {
|
||||||
|
protocol = "https://"
|
||||||
|
parsed = MainSettings.StreamingProxy[8:]
|
||||||
|
} else if strings.Contains(MainSettings.StreamingProxy, "socks5") {
|
||||||
|
protocol = "socks5://"
|
||||||
|
parsed = MainSettings.StreamingProxy[9:]
|
||||||
|
} else if strings.Contains(MainSettings.StreamingProxy, "http") && !strings.Contains(MainSettings.StreamingProxy, "https") {
|
||||||
|
protocol = "http://"
|
||||||
|
parsed = MainSettings.StreamingProxy[7:]
|
||||||
|
} else {
|
||||||
|
log.Fatal("Unsupported protocol")
|
||||||
|
}
|
||||||
|
|
||||||
|
if SettingsTabAppAuthStreamingProxySwitch.Active() == true {
|
||||||
|
sec2, err := keyring.Get(MainSettings.StreamingProxy, MainSettings.StreamingProxyUser)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("Error getting from keyring")
|
||||||
|
}
|
||||||
|
SettingsTabAppStreamingProxyUsernameEntry.SetText(MainSettings.StreamingProxyUser)
|
||||||
|
SettingsTabAppStreamingProxyPassEntry.SetText(sec2)
|
||||||
|
|
||||||
|
fullURL := protocol + MainSettings.StreamingProxyUser + ":" + sec2 + "@" + parsed
|
||||||
|
//adding the proxy settings to the Transport object
|
||||||
|
parsedurl, _ := url.Parse(fullURL)
|
||||||
|
|
||||||
|
HttpSettings = &http.Client{Timeout: time.Second * 10, Transport: &http.Transport{Proxy: http.ProxyURL(parsedurl)}}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// fullURL := protocol + MainSettings.StreamingProxyUser + "@" + parsed
|
||||||
|
parsedurl, _ := url.Parse(MainSettings.StreamingProxy)
|
||||||
|
// temptrans :=
|
||||||
|
HttpSettings = &http.Client{Timeout: time.Second * 10, Transport: &http.Transport{Proxy: http.ProxyURL(parsedurl)}}
|
||||||
|
}
|
||||||
|
|
||||||
|
//adding the Transport object to the http Client
|
||||||
|
|
||||||
|
// HttpSettings.Transport
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingsTabAppMetadataProxySwitch.SetActive(MainSettings.UseMetadataProxy)
|
||||||
|
if MainSettings.UseMetadataProxy == true {
|
||||||
|
SettingsTabAppMetadataProxyEntry.SetText(MainSettings.MetadataProxy)
|
||||||
|
|
||||||
|
// var temptrans2 *http.Transport
|
||||||
|
var protocol, parsed string
|
||||||
|
|
||||||
|
if strings.Contains(MainSettings.MetadataProxy, "https") {
|
||||||
|
protocol = "https://"
|
||||||
|
parsed = MainSettings.MetadataProxy[8:]
|
||||||
|
} else if strings.Contains(MainSettings.MetadataProxy, "socks5") {
|
||||||
|
protocol = "socks5://"
|
||||||
|
parsed = MainSettings.MetadataProxy[9:]
|
||||||
|
} else if strings.Contains(MainSettings.MetadataProxy, "http") && !strings.Contains(MainSettings.MetadataProxy, "https") {
|
||||||
|
protocol = "http://"
|
||||||
|
parsed = MainSettings.MetadataProxy[7:]
|
||||||
|
} else {
|
||||||
|
log.Fatal("Unsupported protocol")
|
||||||
|
}
|
||||||
|
|
||||||
|
if SettingsTabAppAuthMetadataProxySwitch.Active() == true {
|
||||||
|
sec3, err := keyring.Get(MainSettings.MetadataProxy, MainSettings.MetadataProxyUser)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("Error getting from keyring")
|
||||||
|
}
|
||||||
|
SettingsTabAppMetadataProxyUsernameEntry.SetText(MainSettings.MetadataProxyUser)
|
||||||
|
SettingsTabAppMetadataProxyPassEntry.SetText(sec3)
|
||||||
|
|
||||||
|
fullURL := protocol + MainSettings.MetadataProxyUser + ":" + sec3 + "@" + parsed
|
||||||
|
//adding the proxy settings to the Transport object
|
||||||
|
parsedurl2, err := url.Parse(fullURL)
|
||||||
|
MetadataClient = &http.Client{Timeout: time.Second * 10, Transport: &http.Transport{Proxy: http.ProxyURL(parsedurl2)}}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// fullURL := protocol + MainSettings.StreamingProxyUser + "@" + parsed
|
||||||
|
parsedurl2, _ := url.Parse(MainSettings.MetadataProxy)
|
||||||
|
MetadataClient = &http.Client{Timeout: time.Second * 10, Transport: &http.Transport{Proxy: http.ProxyURL(parsedurl2)}}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
//adding the Transport object to the http Client
|
||||||
|
|
||||||
|
}
|
||||||
|
if MainSettings.Codec == "opus" {
|
||||||
|
SettingsTabAppCodecComboBoxTest.SetActive(0)
|
||||||
|
} else if MainSettings.Codec == "flac" {
|
||||||
|
SettingsTabAppCodecComboBoxTest.SetActive(1)
|
||||||
|
} else if MainSettings.Codec == "ogg" {
|
||||||
|
SettingsTabAppCodecComboBoxTest.SetActive(2)
|
||||||
|
} else if MainSettings.Codec == "mp3" {
|
||||||
|
SettingsTabAppCodecComboBoxTest.SetActive(3)
|
||||||
|
}
|
||||||
|
|
||||||
|
// HERE
|
||||||
|
|
||||||
|
if MainSettings.MetadataType == "None" {
|
||||||
|
SettingsTabAppMetadataComboBoxTest.SetActive(0)
|
||||||
|
} else if MainSettings.MetadataType == "Server" {
|
||||||
|
SettingsTabAppMetadataComboBoxTest.SetActive(1)
|
||||||
|
} else if MainSettings.MetadataType == "Sonically" {
|
||||||
|
SettingsTabAppMetadataComboBoxTest.SetActive(2)
|
||||||
|
}
|
||||||
|
|
||||||
|
if MainSettings.HideHeaderBar {
|
||||||
|
MainWindow.SetDecorated(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Print("Sucessfully set user from deserialzed content")
|
||||||
|
cacheDir := GetRunDir() + MainDir + DataDir + "/.cache"
|
||||||
|
CurrentCache, err = fscache.New(cacheDir, 0755, time.Hour*24)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
MainDirC := GetRunDir() + MainDir
|
||||||
|
if _, err := os.Stat(MainDir); os.IsNotExist(err) {
|
||||||
|
os.Mkdir(MainDirC, 0744)
|
||||||
|
os.Mkdir(MainDirC+DataDir, 0744)
|
||||||
|
os.Mkdir(MainDirC+InfDir, 0744)
|
||||||
|
os.Mkdir(MainDirC+SettingsDir, 0744)
|
||||||
|
fmt.Println("Directory tree created")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,243 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/TheCreeper/go-notify"
|
||||||
|
"github.com/delucks/go-subsonic"
|
||||||
|
"github.com/diamondburned/gotk4/pkg/gtk/v4"
|
||||||
|
// "github.com/diamondburned/gotk4/pkg/gdk/v4"
|
||||||
|
// "github.com/diamondburned/gotk4/pkg/core/glib"
|
||||||
|
"github.com/djherbis/fscache"
|
||||||
|
// "github.com/emirpasic/gods/lists/arraylist"
|
||||||
|
"github.com/gordonklaus/portaudio"
|
||||||
|
"github.com/hraban/opus"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetRunDir() string {
|
||||||
|
rundirGet, _ := os.UserHomeDir()
|
||||||
|
return rundirGet
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dirs
|
||||||
|
const MainDir string = "/.Sonically"
|
||||||
|
const DataDir string = "/data"
|
||||||
|
const SettingsDir string = "/Settings"
|
||||||
|
const InfDir string = "/inf"
|
||||||
|
|
||||||
|
var CurrentClient subsonic.Client
|
||||||
|
var CurrentStream *portaudio.Stream
|
||||||
|
var MainQueue []string
|
||||||
|
var NowPlayingIndex int = 0
|
||||||
|
var UserList [10]User
|
||||||
|
var UserDeserialized User
|
||||||
|
var MainSettings Settings
|
||||||
|
var DownloadSyncGroup sync.WaitGroup
|
||||||
|
var TotalTime int = 0
|
||||||
|
var DrawnTime float64 = float64(0.0)
|
||||||
|
var Out []int16 = make([]int16, 1920)
|
||||||
|
|
||||||
|
// var ReadBuffer []byte
|
||||||
|
// var MasterStramSlice map[string]SonicallyStream
|
||||||
|
var CachingMap map[string]bool = make(map[string]bool)
|
||||||
|
|
||||||
|
var TimeInPrecent float64 = 0.0
|
||||||
|
var CurrentCache fscache.Cache
|
||||||
|
|
||||||
|
var AudioControlChan chan bool
|
||||||
|
var OpusStr *opus.Stream
|
||||||
|
var ReadMaster fscache.ReadAtCloser
|
||||||
|
var WriteMaster io.WriteCloser
|
||||||
|
|
||||||
|
var ArtistsCached []string
|
||||||
|
var PlaylistCached []string
|
||||||
|
var TempArtist string = ""
|
||||||
|
var EnterArtist bool
|
||||||
|
|
||||||
|
// UI widgets declaration
|
||||||
|
var MainWindow *gtk.ApplicationWindow
|
||||||
|
var LyricsNotebook *gtk.Notebook
|
||||||
|
var LyricsScrolledWindow *gtk.ScrolledWindow
|
||||||
|
var MainFlowbox *gtk.FlowBox
|
||||||
|
var MainFlowBoxChild1 *gtk.FlowBoxChild
|
||||||
|
var MainFlowBoxChild2 *gtk.FlowBoxChild
|
||||||
|
var MainFlowBoxChild3 *gtk.FlowBoxChild
|
||||||
|
var MainFlowBoxChild4 *gtk.FlowBoxChild
|
||||||
|
var MainGridFlowBoxChild2 *gtk.Grid
|
||||||
|
var MainGridFlowBoxChild4 *gtk.Grid
|
||||||
|
var MainGridFlowBoxChild2Grid *gtk.Grid
|
||||||
|
var MainLeftSeparator *gtk.Separator
|
||||||
|
var MainRightSeparator *gtk.Separator
|
||||||
|
var MainScale *gtk.Scale
|
||||||
|
var MainScaleGrid *gtk.Grid
|
||||||
|
|
||||||
|
var QueueMasterBox *gtk.Box
|
||||||
|
var QueueSearchControlBox *gtk.Grid
|
||||||
|
var QueueFlowBox *gtk.FlowBox
|
||||||
|
var QueueScrolledWindow *gtk.ScrolledWindow
|
||||||
|
var QueueSaveButton *gtk.Button
|
||||||
|
var QueueClearButton *gtk.Button
|
||||||
|
var QueueShuffleButton *gtk.Button
|
||||||
|
var QueueFlowBoxChild1 *gtk.FlowBoxChild
|
||||||
|
|
||||||
|
var SearchFlowBox *gtk.FlowBox
|
||||||
|
var SearchFlowBoxChild1 *gtk.FlowBoxChild
|
||||||
|
var SearchMasterBox *gtk.Box
|
||||||
|
var SearchControlBox *gtk.Grid
|
||||||
|
var SearchScrolledWindow *gtk.ScrolledWindow
|
||||||
|
|
||||||
|
var LibraryFlowBox1 *gtk.FlowBox
|
||||||
|
var LibraryFlowBox2 *gtk.FlowBox
|
||||||
|
var LibraryFlowBox3 *gtk.FlowBox
|
||||||
|
var LibraryFlowBox4 *gtk.FlowBox
|
||||||
|
|
||||||
|
var LibraryPlaylistMainBox *gtk.Box
|
||||||
|
var LibraryStarredMainBox *gtk.Box
|
||||||
|
var LibraryLibraryMainBox *gtk.Box
|
||||||
|
var LibraryPodcastMainBox *gtk.Box
|
||||||
|
var LibraryFlowBox1Child1 *gtk.FlowBoxChild
|
||||||
|
var LibraryFlowBox2Child1 *gtk.FlowBoxChild
|
||||||
|
var LibraryFlowBox3Child1 *gtk.FlowBoxChild
|
||||||
|
var LibraryFlowBox4Child1 *gtk.FlowBoxChild
|
||||||
|
var LibraryPlaylistMainControlGrid *gtk.Grid
|
||||||
|
var LibraryStarredMainControlGrid *gtk.Grid
|
||||||
|
var LibraryLibraryMainControlGrid *gtk.Grid
|
||||||
|
var LibraryPodcastMainControlGrid *gtk.Grid
|
||||||
|
var LibraryPlaylistBackButton *gtk.Button
|
||||||
|
var LibraryPlaylistRefreshButton *gtk.Button
|
||||||
|
var LibraryStarredBackButton *gtk.Button
|
||||||
|
var LibraryStarredRefreshButton *gtk.Button
|
||||||
|
var LibraryLibraryBackButton *gtk.Button
|
||||||
|
var LibraryLibraryRefreshButton *gtk.Button
|
||||||
|
var LibraryPodcastBackButton *gtk.Button
|
||||||
|
var LibraryPodcastRefreshButton *gtk.Button
|
||||||
|
var LibraryStarredListBox *gtk.ListBox
|
||||||
|
var LibraryPodcastListBox *gtk.ListBox
|
||||||
|
var LibraryPlaylistMainScrolledWindow *gtk.ScrolledWindow
|
||||||
|
var LibraryPodcastListBoxScrolledWindow *gtk.ScrolledWindow
|
||||||
|
var LibraryStarredListBoxScrolledWindow *gtk.ScrolledWindow
|
||||||
|
var QueueListBoxLibraryScrolledWindow *gtk.ScrolledWindow
|
||||||
|
|
||||||
|
var SettingsFlowBox *gtk.FlowBox
|
||||||
|
var SettingsTabUserUsernameEntry *gtk.Entry
|
||||||
|
var SettingsTabUserServerEntry *gtk.Entry
|
||||||
|
var SettingsTabUserDeviceNameEntry *gtk.Entry
|
||||||
|
var SettingsTabUserPasswordEntry *gtk.Entry
|
||||||
|
var SettingsTabUserStatusLabel *gtk.Label
|
||||||
|
var SearchTabSearchEntry *gtk.SearchEntry
|
||||||
|
var SearchTabSongCheckButton *gtk.CheckButton
|
||||||
|
var SearchTabAlbumCheckButton *gtk.CheckButton
|
||||||
|
var SearchTabArtistCheckButton *gtk.CheckButton
|
||||||
|
var SearchListBoxResult *gtk.ListBox
|
||||||
|
var PlayListListBox *gtk.ListBox
|
||||||
|
var MainSongTitleLabel *gtk.Label
|
||||||
|
var MainArtistLabel *gtk.Label
|
||||||
|
var MainAlbumLabel *gtk.Label
|
||||||
|
var MainTimeDrawnLabel *gtk.Label
|
||||||
|
var MainTimeLeft *gtk.Label
|
||||||
|
var MainAdjustment *gtk.Adjustment
|
||||||
|
var SettingsTabAppDeleteSwitch *gtk.Switch
|
||||||
|
var SettingsTabAppCodecComboBoxTest *gtk.ComboBoxText
|
||||||
|
|
||||||
|
// var SettingsTabAppRetrievingComboBoxText *gtk.ComboBoxText
|
||||||
|
var SettingsTabAppCacheEntry *gtk.Entry
|
||||||
|
var QueueListBox *gtk.ListBox
|
||||||
|
var MainAlbumCover *gtk.Image
|
||||||
|
var MainPlayPauseButton *gtk.Button
|
||||||
|
var MainPreviousButton *gtk.Button
|
||||||
|
var MainNextButton *gtk.Button
|
||||||
|
var MainRepeatButton *gtk.Button
|
||||||
|
var MainStarredButton *gtk.Button
|
||||||
|
var MainSubtitlesTextView *gtk.TextView
|
||||||
|
var MainTotalTimeLabel *gtk.Label
|
||||||
|
var LibraryNotebook *gtk.Notebook
|
||||||
|
var MainNotebook *gtk.Notebook
|
||||||
|
var QueueListBoxLibrary *gtk.ListBox
|
||||||
|
var SettingsTabAppCoverSizeEntry *gtk.Entry
|
||||||
|
var SettingsTabAppBlurRadiusEntry *gtk.Entry
|
||||||
|
var SettingsTabAppBitrateEntry *gtk.Entry
|
||||||
|
var SettingsTabAppBufferSizeEntry *gtk.Entry
|
||||||
|
var SettingsTabAppStreamingProxySwitch *gtk.Switch
|
||||||
|
var SettingsTabAppStreamingProxyEntry *gtk.Entry
|
||||||
|
var SettingsTabAppAuthStreamingProxySwitch *gtk.Switch
|
||||||
|
var SettingsTabAppStreamingProxyUsernameEntry *gtk.Entry
|
||||||
|
var SettingsTabAppStreamingProxyPassEntry *gtk.Entry
|
||||||
|
|
||||||
|
var SettingsTabAppMetadataProxySwitch *gtk.Switch
|
||||||
|
var SettingsTabAppMetadataProxyEntry *gtk.Entry
|
||||||
|
var SettingsTabAppAuthMetadataProxySwitch *gtk.Switch
|
||||||
|
var SettingsTabAppMetadataProxyUsernameEntry *gtk.Entry
|
||||||
|
var SettingsTabAppMetadataProxyPassEntry *gtk.Entry
|
||||||
|
|
||||||
|
var SettingsAccountsFlowBox *gtk.FlowBox
|
||||||
|
var SettingsAccountFlowboxChild1 *gtk.FlowBoxChild
|
||||||
|
var SettingsAccountFlowboxChild2 *gtk.FlowBoxChild
|
||||||
|
|
||||||
|
var SettingsNotebook *gtk.Notebook
|
||||||
|
var SettingsAccountBox *gtk.Box
|
||||||
|
var SettingsAccountLoginGrid *gtk.Grid
|
||||||
|
var SettingsAccountButtonsGrid *gtk.Grid
|
||||||
|
var SettingsAccountUserListbox *gtk.ListBox
|
||||||
|
var SettingsAppFlowBox *gtk.FlowBox
|
||||||
|
var SettingsCreditsFlowBox *gtk.FlowBox
|
||||||
|
|
||||||
|
var SettingsAccountUsernameLabel *gtk.Label
|
||||||
|
var SettingsAccountPasswordLabel *gtk.Label
|
||||||
|
var SettingsAccountPLayerNameLabel *gtk.Label
|
||||||
|
var SettingsAccountServerLabel *gtk.Label
|
||||||
|
|
||||||
|
var SettingsAccountTestButton *gtk.Button
|
||||||
|
var SettingsAccountSaveButton *gtk.Button
|
||||||
|
var SettingsAccountLoadButton *gtk.Button
|
||||||
|
|
||||||
|
// var SettingsAppScrolledWindow *gtk.ScrolledWindow
|
||||||
|
var SettingsAppFlowBoxChild1 *gtk.FlowBoxChild
|
||||||
|
var SettingsAppFlowBoxChild2 *gtk.FlowBoxChild
|
||||||
|
var SettingsAppFlowBoxChild3 *gtk.FlowBoxChild
|
||||||
|
var SettingsAppFlowBoxChild4 *gtk.FlowBoxChild
|
||||||
|
var SettingsAppGrid1 *gtk.Grid
|
||||||
|
var SettingsAppGrid2 *gtk.Grid
|
||||||
|
var SettingsAppGrid3 *gtk.Grid
|
||||||
|
var SettingsAppGrid4 *gtk.Grid
|
||||||
|
|
||||||
|
var SettingsAppLabelGrid4 *gtk.Label
|
||||||
|
var SettingsAppLabelGrid4Metadata *gtk.Label
|
||||||
|
var SettingsTabAppMetadataComboBoxTest *gtk.ComboBoxText
|
||||||
|
|
||||||
|
var SettingsAppLabelGrid1 *gtk.Label
|
||||||
|
var SettingsAppLabelGrid2 *gtk.Label
|
||||||
|
var SettingsAppLabelGrid3 *gtk.Label
|
||||||
|
|
||||||
|
var SettingsAppLabelGrid1DeleteOnClose *gtk.Label
|
||||||
|
var SettingsAppLabelGrid1CoverSideSize *gtk.Label
|
||||||
|
var SettingsAppLabelGrid1BackgroundBlurRadius *gtk.Label
|
||||||
|
|
||||||
|
var SettingsAppLabelGrid2Codec *gtk.Label
|
||||||
|
var SettingsAppLabelGrid2Bitrate *gtk.Label
|
||||||
|
var SettingsAppLabelGrid2DownloadBufferSize *gtk.Label
|
||||||
|
|
||||||
|
var SettingsTabAppHideHeaderBarSwitch *gtk.Switch
|
||||||
|
var SettingsAppLabelHideHeaderBar *gtk.Label
|
||||||
|
|
||||||
|
var SettingsAccountScrolledWindow *gtk.ScrolledWindow
|
||||||
|
var SettingsAppScrolledWindow *gtk.ScrolledWindow
|
||||||
|
var SettingsCreditsScrolledWindow *gtk.ScrolledWindow
|
||||||
|
|
||||||
|
var SettingsAppLabelGrid3UseProxyForStreaming *gtk.Label
|
||||||
|
var SettingsAppLabelGrid3AuthenticateStreamingProxy *gtk.Label
|
||||||
|
var SettingsAppLabelGrid3UseProxyForMetadata *gtk.Label
|
||||||
|
var SettingsAppLabelGrid3AuthenticateMetadataProxy *gtk.Label
|
||||||
|
var SettingsCreditsFlowBoxChild1 *gtk.FlowBoxChild
|
||||||
|
var SettingsCreditsLabel1 *gtk.Label
|
||||||
|
|
||||||
|
var MetadataClient *http.Client
|
||||||
|
var HttpSettings *http.Client
|
||||||
|
|
||||||
|
// var one gtk.EventBox
|
||||||
|
|
||||||
|
// LIBNOTIFY
|
||||||
|
|
||||||
|
var MainNotification notify.Notification
|
|
@ -0,0 +1,59 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
// Slice Utils instead of ArrayList
|
||||||
|
|
||||||
|
// Returns Index of T
|
||||||
|
func SliceIndexOf[T comparable](slc []T, el T) int {
|
||||||
|
for i, x := range slc {
|
||||||
|
if x == el {
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
// Swap T1 and T2 at indexes
|
||||||
|
func SliceSwapAtIndexes[T comparable](slc []T, el int, el2 int) {
|
||||||
|
x := slc[el]
|
||||||
|
slc[el] = slc[el2]
|
||||||
|
slc[el2] = x
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns true if Slice contains T
|
||||||
|
func SliceContains[T comparable](slc []T, el T) bool {
|
||||||
|
for _, x := range slc {
|
||||||
|
if x == el {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove T if exists
|
||||||
|
func SliceRemove[T comparable](slc []T, el T) []T {
|
||||||
|
res := SliceIndexOf(slc, el)
|
||||||
|
if res != -1 {
|
||||||
|
return SliceRemoveAt(slc, res)
|
||||||
|
}
|
||||||
|
return slc
|
||||||
|
}
|
||||||
|
|
||||||
|
func SliceRemoveAt[T comparable](slc []T, el int) []T {
|
||||||
|
for i := el; i <= len(slc)-2; i++ {
|
||||||
|
slc[i] = slc[i+1]
|
||||||
|
}
|
||||||
|
// x:= (len(slc)-2)
|
||||||
|
// slc = append([]T(nil), slc[:x]...)
|
||||||
|
return slc[:(len(slc) - 1)]
|
||||||
|
}
|
||||||
|
|
||||||
|
func SliceInsertAt[T comparable](slc []T, el T, pos int) {
|
||||||
|
slc = append(slc, slc[len(slc)-1])
|
||||||
|
for i := len(slc) - 2; i > pos; i-- {
|
||||||
|
slc[i] = slc[i-1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func SliceClear[T comparable](slc []T) []T {
|
||||||
|
return []T{}
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
[Desktop Entry]
|
||||||
|
Encoding=UTF-8
|
||||||
|
Version=0.4.1
|
||||||
|
Type=Application
|
||||||
|
Terminal=false
|
||||||
|
Exec=/usr/bin/sonically
|
||||||
|
Name=Sonically
|
||||||
|
Icon=/usr/share/pixmaps/sonically.svg
|
|
@ -0,0 +1,110 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
// "net/http"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
|
|
||||||
|
"github.com/TheCreeper/go-notify"
|
||||||
|
"github.com/diamondburned/gotk4/pkg/gtk/v4"
|
||||||
|
"github.com/hashicorp/go-retryablehttp"
|
||||||
|
// "github.com/diamondburned/gotk4/pkg/core/glib"
|
||||||
|
// "github.com/hashicorp/go-retryablehttp"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Widely used, conditionally created objects
|
||||||
|
var ifDelete bool = true
|
||||||
|
|
||||||
|
var delDir string
|
||||||
|
var settingsPath string = (GetRunDir() + MainDir + SettingsDir + "/gob")
|
||||||
|
|
||||||
|
// var mainWaitGroup sync.WaitGroup
|
||||||
|
|
||||||
|
// This is where app starts
|
||||||
|
func main() {
|
||||||
|
app := gtk.NewApplication("eu.itmodulo.sonically", 0)
|
||||||
|
app.ConnectActivate(func() { activate(app) })
|
||||||
|
|
||||||
|
if code := app.Run(os.Args); code > 0 {
|
||||||
|
os.Exit(code)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is func that initates GUI, deserializez data
|
||||||
|
func activate(app *gtk.Application) {
|
||||||
|
AudioControlChan = make(chan bool) // Initate goroutine channel
|
||||||
|
MainWindow = gtk.NewApplicationWindow(app)
|
||||||
|
MainWindow.SetTitle("Sonically")
|
||||||
|
SetUpGui()
|
||||||
|
|
||||||
|
x := retryablehttp.NewClient()
|
||||||
|
x.RetryMax = 15
|
||||||
|
x.Logger = nil
|
||||||
|
|
||||||
|
y := retryablehttp.NewClient()
|
||||||
|
y.RetryMax = 20
|
||||||
|
y.Logger = nil
|
||||||
|
// All widgets put together
|
||||||
|
HttpSettings = x.StandardClient() // Default client for connection
|
||||||
|
MetadataClient = y.StandardClient() // Default client for connection
|
||||||
|
Init() // Set up account
|
||||||
|
MainWindow.ConnectCloseRequest(closeApp)
|
||||||
|
MainWindow.Resizable()
|
||||||
|
|
||||||
|
MainWindow.Show()
|
||||||
|
|
||||||
|
// Background Read User Library, to speedup later loading
|
||||||
|
if MainSettings.Bitrate != 0 {
|
||||||
|
go DownloadLib()
|
||||||
|
go DownloadPlaylist()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func closeApp() bool {
|
||||||
|
if CurrentCache != nil {
|
||||||
|
CurrentCache.Clean()
|
||||||
|
}
|
||||||
|
if MainSettings.DeleteOnClose {
|
||||||
|
delDir = GetRunDir() + MainDir + DataDir + "/"
|
||||||
|
|
||||||
|
d, err := os.Open(delDir)
|
||||||
|
if err != nil {
|
||||||
|
PanicOnError(err)
|
||||||
|
}
|
||||||
|
defer d.Close()
|
||||||
|
names, err := d.Readdirnames(-1)
|
||||||
|
if err != nil {
|
||||||
|
PanicOnError(err)
|
||||||
|
}
|
||||||
|
for _, name := range names {
|
||||||
|
err = os.RemoveAll(filepath.Join(delDir, name))
|
||||||
|
if err != nil {
|
||||||
|
PanicOnError(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
delDir = GetRunDir() + MainDir + InfDir + "/"
|
||||||
|
|
||||||
|
d, err := os.Open(delDir)
|
||||||
|
if err != nil {
|
||||||
|
PanicOnError(err)
|
||||||
|
}
|
||||||
|
defer d.Close()
|
||||||
|
names, err := d.Readdirnames(-1)
|
||||||
|
if err != nil {
|
||||||
|
PanicOnError(err)
|
||||||
|
}
|
||||||
|
for _, name := range names {
|
||||||
|
err = os.RemoveAll(filepath.Join(delDir, name))
|
||||||
|
if err != nil {
|
||||||
|
PanicOnError(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
notify.CloseNotification(MainNotification.ReplacesID)
|
||||||
|
runtime.GC()
|
||||||
|
return false
|
||||||
|
}
|
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 14 KiB |
Loading…
Reference in New Issue