Skip to content

Commit

Permalink
initial release
Browse files Browse the repository at this point in the history
  • Loading branch information
snowie2000 committed Jun 27, 2024
0 parents commit 0b9f797
Show file tree
Hide file tree
Showing 52 changed files with 3,913 additions and 0 deletions.
219 changes: 219 additions & 0 deletions License.txt

Large diffs are not rendered by default.

29 changes: 29 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# LiveTV

Aggregate IPTV feeds in one station and enjoy!

## Install

A custom compiling is required. Docker image is not available yet.

```bash
git clone https://github.com/snowie2000/livetv
cd livetv
go build
```

## Usage

Default password is "password".

First you need to know how to access your host from the outside, if you are using a VPS or a dedicated server, you can visit `http://your_ip:9500` and you should see the following screen.

![index_page](pic/index-en.png)

First of all, you need to click the gear button and "Auto Fill" in the setting area, set the correct URL, then click "Ok".

Then you can add a channel. After the channel is added successfully, you can play the M3U8 file from the address column.

When you use Kodi or similar player, you can consider using the M3U file URL in the playlist field, and a playlist containing all the channel information will be generated automatically.

To protect your service from unauthorized access, you can set a secret in the settings dialog and then all your playlist and proxy services will need a unique token to access (based on your secret).
76 changes: 76 additions & 0 deletions global/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package global

import (
"encoding/base64"

"github.com/jinzhu/gorm"
"github.com/zjyl1994/livetv/model"
"golang.org/x/crypto/scrypt"
)

func strongKey(key string) []byte {
//make a key strong by using scrypt
dk, _ := scrypt.Key([]byte(key), []byte("dasdADD123@#as84373^!$*&!#$1#12#"), 16384, 8, 1, 32)
return dk
}

func GetConfig(key string) (string, error) {
if confValue, ok := ConfigCache.Load(key); ok {
return confValue, nil
} else {
var value model.Config
err := DB.Where("name = ?", key).First(&value).Error
if err != nil {
if gorm.IsRecordNotFoundError(err) {
return "", ErrConfigNotFound
} else {
return "", err
}
} else {
ConfigCache.Store(key, value.Data)
return value.Data, nil
}
}
}

var strongSecret string = ""
var strongLiveSecret string = ""

func GetSecretToken() string {
if strongSecret == "" {
secret, _ := GetConfig("secret")
if secret == "" {
return ""
}
derived := strongKey(secret)
strongSecret = string([]rune(base64.URLEncoding.EncodeToString(derived))[1:10])
}
return strongSecret
}

func GetLiveToken() string {
if strongLiveSecret == "" {
secret, _ := GetConfig("secret")
if secret == "" {
return ""
}
derived := strongKey(secret+"_live")
strongLiveSecret = string([]rune(base64.URLEncoding.EncodeToString(derived))[1:10])
}
return strongLiveSecret
}

func ClearSecretToken() {
strongSecret = ""
strongLiveSecret = ""
ChannelCache.Clear()
}

func SetConfig(key, value string) error {
data := model.Config{Name: key, Data: value}
err := DB.Save(&data).Error
if err == nil {
ConfigCache.Store(key, value)
}
return err
}
34 changes: 34 additions & 0 deletions global/db.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package global

import (
"github.com/jinzhu/gorm"
_ "github.com/jinzhu/gorm/dialects/sqlite"
"github.com/zjyl1994/livetv/model"
)

var DB *gorm.DB

func InitDB(filepath string) (err error) {
DB, err = gorm.Open("sqlite3", filepath)
if err != nil {
return err
}
err = DB.AutoMigrate(&model.Config{}, &model.Channel{}).Error
if err != nil {
return err
}
for key, valueDefault := range defaultConfigValue {
var valueInDB model.Config
err = DB.Where("name = ?", key).First(&valueInDB).Error
if err != nil {
if gorm.IsRecordNotFoundError(err) {
ConfigCache.Store(key, valueDefault)
} else {
return err
}
} else {
ConfigCache.Store(key, valueInDB.Data)
}
}
return nil
}
26 changes: 26 additions & 0 deletions global/default.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package global

import (
"time"

"github.com/zjyl1994/livetv/model"

"github.com/patrickmn/go-cache"
"github.com/zjyl1994/livetv/syncx"
)

var defaultConfigValue = map[string]string{
"ytdl_cmd": "yt-dlp",
"ytdl_args": "--extractor-args youtube:skip=dash -f b -g {url}",
"base_url": "http://127.0.0.1:9000",
"password": "password",
"apiKey": "",
}

var (
HttpClientTimeout = 10 * time.Second
ConfigCache syncx.Map[string, string]
URLCache syncx.Map[string, *model.LiveInfo]
ChannelCache syncx.Map[uint, model.Channel]
M3U8Cache = cache.New(3*time.Second, 10*time.Second)
)
8 changes: 8 additions & 0 deletions global/error.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package global

import "errors"

var (
ErrConfigNotFound = errors.New("config key not found")
ErrYoutubeDlNotFound = errors.New("Youtube-dl not found")
)
42 changes: 42 additions & 0 deletions global/utils.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// utils
package global

import (
"net/url"
"path"
"strings"
)

func GetBaseURL(rawURL string) string {
parsedURL, err := url.Parse(rawURL)
if err != nil {
return ""
}
parsedURL.RawQuery = ""

// Remove the last element (document) from the path
parsedURL.Path = path.Dir(parsedURL.Path) + "/"

// Rebuild the URL without the document part
return parsedURL.String()
}

func IsValidURL(u string) bool {
_, err := url.ParseRequestURI(u)
if err == nil {
uu, err := url.Parse(u)
return err == nil && uu.Scheme != "" && uu.Host != ""
}
return false
}

func MergeUrl(baseUrl string, partialUrl string) string {
if strings.HasPrefix(partialUrl, "/") {
u, _ := url.Parse(baseUrl)
u.Path = ""
u.RawQuery = ""
u.Fragment = ""
return u.String() + partialUrl
}
return baseUrl + partialUrl
}
94 changes: 94 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
module github.com/zjyl1994/livetv

go 1.21

toolchain go1.21.0

require (
github.com/dlclark/regexp2 v1.10.0
github.com/fopina/net-proxy-httpconnect v0.0.0-20230320235234-11f65320b851
github.com/gin-contrib/sessions v0.0.3
github.com/gin-gonic/gin v1.9.1
github.com/grafov/m3u8 v0.12.0
github.com/imroc/req/v3 v3.43.1
github.com/jinzhu/gorm v1.9.16
github.com/joho/godotenv v1.3.0
github.com/mojocn/base64Captcha v1.3.6
github.com/nareix/joy5 v0.0.0-20210317075623-2c912ca30590
github.com/patrickmn/go-cache v2.1.0+incompatible
github.com/robfig/cron/v3 v3.0.1
github.com/sosodev/duration v1.2.0
golang.org/x/crypto v0.21.0
golang.org/x/net v0.22.0
golang.org/x/text v0.14.0
google.golang.org/api v0.163.0
)

require (
cloud.google.com/go/compute v1.23.3 // indirect
cloud.google.com/go/compute/metadata v0.2.3 // indirect
github.com/andybalholm/brotli v1.1.0 // indirect
github.com/bytedance/sonic v1.10.1 // indirect
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect
github.com/chenzhuoyu/iasm v0.9.0 // indirect
github.com/cloudflare/circl v1.3.7 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/go-logr/logr v1.4.1 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.15.5 // indirect
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
github.com/goccy/go-json v0.10.2 // indirect
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/google/pprof v0.0.0-20240227163752-401108e1b7e7 // indirect
github.com/google/s2a-go v0.1.7 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect
github.com/googleapis/gax-go/v2 v2.12.0 // indirect
github.com/gorilla/context v1.1.1 // indirect
github.com/gorilla/securecookie v1.1.1 // indirect
github.com/gorilla/sessions v1.1.3 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/compress v1.17.7 // indirect
github.com/klauspost/cpuid/v2 v2.2.5 // indirect
github.com/kr/pretty v0.3.1 // indirect
github.com/leodido/go-urn v1.2.4 // indirect
github.com/mattn/go-isatty v0.0.19 // indirect
github.com/mattn/go-sqlite3 v1.14.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/onsi/ginkgo/v2 v2.16.0 // indirect
github.com/pelletier/go-toml/v2 v2.1.0 // indirect
github.com/quic-go/qpack v0.4.0 // indirect
github.com/quic-go/quic-go v0.41.0 // indirect
github.com/refraction-networking/utls v1.6.3 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.2.11 // indirect
go.opencensus.io v0.24.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.47.0 // indirect
go.opentelemetry.io/otel v1.22.0 // indirect
go.opentelemetry.io/otel/metric v1.22.0 // indirect
go.opentelemetry.io/otel/trace v1.22.0 // indirect
go.uber.org/mock v0.4.0 // indirect
golang.org/x/arch v0.5.0 // indirect
golang.org/x/exp v0.0.0-20240222234643-814bf88cf225 // indirect
golang.org/x/image v0.13.0 // indirect
golang.org/x/mod v0.16.0 // indirect
golang.org/x/oauth2 v0.16.0 // indirect
golang.org/x/sys v0.18.0 // indirect
golang.org/x/tools v0.19.0 // indirect
google.golang.org/appengine v1.6.8 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240125205218-1f4bbc51befe // indirect
google.golang.org/grpc v1.61.0 // indirect
google.golang.org/protobuf v1.32.0 // indirect
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
Loading

0 comments on commit 0b9f797

Please sign in to comment.