-
Notifications
You must be signed in to change notification settings - Fork 31
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 0b9f797
Showing
52 changed files
with
3,913 additions
and
0 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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). |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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") | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
) |
Oops, something went wrong.