Skip to content

Commit

Permalink
Merge pull request #5 from sonroyaalmerol/use-sqlite
Browse files Browse the repository at this point in the history
Switch to SQLite
  • Loading branch information
sonroyaalmerol authored Mar 1, 2024
2 parents bedcf18 + e55fb5b commit e7d93c3
Show file tree
Hide file tree
Showing 10 changed files with 247 additions and 59 deletions.
7 changes: 6 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,12 @@ RUN go mod download
COPY . .

# Build the Go app
RUN go build -o main .
ENV CGO_ENABLED=1
RUN apk add --no-cache gcc musl-dev
RUN go build -ldflags='-s -w -extldflags "-static"' -o main .

# Run tests
RUN go test ./...

####################

Expand Down
52 changes: 52 additions & 0 deletions database/database_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package database

import (
"testing"
"os"
"path/filepath"
)

func TestSaveAndLoadFromSQLite(t *testing.T) {
sqliteDBPath := filepath.Join(".", "data", "database.sqlite")

// Test InitializeSQLite and check if the database file exists
err := InitializeSQLite()
if err != nil {
t.Errorf("InitializeSQLite returned error: %v", err)
}
defer os.Remove(sqliteDBPath) // Cleanup the database file after the test

// Test LoadFromSQLite with existing data in the database
expected := []StreamInfo{{
Title: "stream1",
TvgID: "test1",
LogoURL: "http://test.com/image.png",
Group: "test",
URLs: []StreamURL{{
Content: "testing",
M3UIndex: 1,
}},
}, {
Title: "stream2",
TvgID: "test2",
LogoURL: "http://test2.com/image.png",
Group: "test2",
URLs: []StreamURL{{
Content: "testing2",
M3UIndex: 2,
}},
}}
err = SaveToSQLite(expected) // Insert test data into the database
if err != nil {
t.Errorf("SaveToSQLite returned error: %v", err)
}

result, err := LoadFromSQLite()
if err != nil {
t.Errorf("LoadFromSQLite returned error: %v", err)
}

if len(result) != len(expected) {
t.Errorf("LoadFromSQLite returned %+v, expected %+v", result, expected)
}
}
153 changes: 153 additions & 0 deletions database/db.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
package database

import (
"database/sql"
"fmt"
"os"
"path/filepath"

_ "github.com/mattn/go-sqlite3"
)

var db *sql.DB

func InitializeSQLite() error {
foldername := filepath.Join(".", "data")
filename := filepath.Join(foldername, "database.sqlite")

err := os.MkdirAll(foldername, 0755)
if err != nil {
return fmt.Errorf("error creating data folder: %v\n", err)
}

file, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE, 0666)
if err != nil {
return fmt.Errorf("error creating database file: %v\n", err)
}
file.Close()

db, err = sql.Open("sqlite3", filename)
if err != nil {
return fmt.Errorf("error opening SQLite database: %v\n", err)
}

// Create table if not exists
_, err = db.Exec(`
CREATE TABLE IF NOT EXISTS streams (
id INTEGER PRIMARY KEY AUTOINCREMENT,
title TEXT UNIQUE,
tvg_id TEXT,
logo_url TEXT,
group_name TEXT
)
`)
if err != nil {
return fmt.Errorf("error creating table: %v\n", err)
}

_, err = db.Exec(`
CREATE TABLE IF NOT EXISTS stream_urls (
id INTEGER PRIMARY KEY AUTOINCREMENT,
stream_id INTEGER,
content TEXT,
m3u_index INTEGER,
FOREIGN KEY(stream_id) REFERENCES streams(id)
)
`)
if err != nil {
return fmt.Errorf("error creating table: %v\n", err)
}

return nil
}

func SaveToSQLite(streams []StreamInfo) error {
tx, err := db.Begin()
if err != nil {
return fmt.Errorf("error beginning transaction: %v", err)
}
defer tx.Rollback()

stmt, err := tx.Prepare("INSERT INTO streams(title, tvg_id, logo_url, group_name) VALUES(?, ?, ?, ?)")
if err != nil {
return fmt.Errorf("error preparing statement: %v", err)
}
defer stmt.Close()

for _, s := range streams {
res, err := stmt.Exec(s.Title, s.TvgID, s.LogoURL, s.Group)
if err != nil {
return fmt.Errorf("error inserting stream: %v", err)
}

streamID, err := res.LastInsertId()
if err != nil {
return fmt.Errorf("error getting last inserted ID: %v", err)
}

urlStmt, err := tx.Prepare("INSERT INTO stream_urls(stream_id, content, m3u_index) VALUES(?, ?, ?)")
if err != nil {
return fmt.Errorf("error preparing statement: %v", err)
}
defer urlStmt.Close()

for _, u := range s.URLs {
_, err := urlStmt.Exec(streamID, u.Content, u.M3UIndex)
if err != nil {
return fmt.Errorf("error inserting stream URL: %v", err)
}
}
}

err = tx.Commit()
if err != nil {
return fmt.Errorf("error committing transaction: %v", err)
}

return nil
}

func LoadFromSQLite() ([]StreamInfo, error) {
rows, err := db.Query("SELECT id, title, tvg_id, logo_url, group_name FROM streams")
if err != nil {
return nil, fmt.Errorf("error querying streams: %v", err)
}
defer rows.Close()

var streams []StreamInfo
for rows.Next() {
var s StreamInfo
var streamId int
err := rows.Scan(&streamId, &s.Title, &s.TvgID, &s.LogoURL, &s.Group)
if err != nil {
return nil, fmt.Errorf("error scanning stream: %v", err)
}

urlRows, err := db.Query("SELECT content, m3u_index FROM stream_urls WHERE stream_id = ?", streamId)
if err != nil {
return nil, fmt.Errorf("error querying stream URLs: %v", err)
}
defer urlRows.Close()

var urls []StreamURL
for urlRows.Next() {
var u StreamURL
err := urlRows.Scan(&u.Content, &u.M3UIndex)
if err != nil {
return nil, fmt.Errorf("error scanning stream URL: %v", err)
}
urls = append(urls, u)
}
if err := urlRows.Err(); err != nil {
return nil, fmt.Errorf("error iterating over URL rows: %v", err)
}

s.URLs = urls
streams = append(streams, s)
}
if err := rows.Err(); err != nil {
return nil, fmt.Errorf("error iterating over rows: %v", err)
}

return streams, nil
}
2 changes: 1 addition & 1 deletion m3u/types.go → database/types.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package m3u
package database

type StreamInfo struct {
Title string
Expand Down
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,5 @@ module m3u-stream-merger
go 1.21.5

require github.com/satori/go.uuid v1.2.0

require github.com/mattn/go-sqlite3 v1.14.22 // indirect
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
5 changes: 3 additions & 2 deletions m3u/generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"fmt"
"log"
"m3u-stream-merger/utils"
"m3u-stream-merger/database"
"net/http"
)

Expand Down Expand Up @@ -40,12 +41,12 @@ func GenerateM3UContent(w http.ResponseWriter, r *http.Request) {
}
}

func FindStreamByName(streamName string) (*StreamInfo, error) {
func FindStreamByName(streamName string) (*database.StreamInfo, error) {
for _, s := range Streams {
if s.Title == streamName {
return &s, nil
}
}

return &StreamInfo{}, errors.New("stream not found")
return &database.StreamInfo{}, errors.New("stream not found")
}
8 changes: 6 additions & 2 deletions m3u/global.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
package m3u

var Streams []StreamInfo
var NewStreams []StreamInfo
import (
"m3u-stream-merger/database"
)

var Streams []database.StreamInfo
var NewStreams []database.StreamInfo
36 changes: 22 additions & 14 deletions m3u/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,30 @@ import (
"regexp"
"strings"
"sync"

"m3u-stream-merger/database"
)

// GetStreams retrieves and merges stream information from multiple M3U files.
func GetStreams(skipClearing bool) error {
// Initialize database
err := database.InitializeSQLite()
if err != nil {
return fmt.Errorf("InitializeSQLite error: %v", err)
}

if !skipClearing {
// init
log.Println("Loading from JSON...")
fromJson, err := loadFromJSON()
log.Println("Loading from database...")
fromDB, err := database.LoadFromSQLite()
if err == nil {
Streams = fromJson
Streams = fromDB

return nil
}
}

err := loadM3UFiles(skipClearing)
err = loadM3UFiles(skipClearing)
if err != nil {
return fmt.Errorf("loadM3UFiles error: %v", err)
}
Expand Down Expand Up @@ -60,20 +68,20 @@ func GetStreams(skipClearing bool) error {

Streams = NewStreams

fmt.Print("Saving to JSON...\n")
_ = saveToJSON(Streams)
fmt.Print("Saving to database...\n")
_ = database.SaveToSQLite(Streams)

return nil
}

// mergeStreamInfo merges two slices of StreamInfo based on Title.
func mergeStreamInfo(existing, new []StreamInfo) []StreamInfo {
// mergeStreamInfo merges two slices of database.StreamInfo based on Title.
func mergeStreamInfo(existing, new []database.StreamInfo) []database.StreamInfo {
var wg sync.WaitGroup
var mutex sync.Mutex

for _, stream := range new {
wg.Add(1)
go func(s StreamInfo) {
go func(s database.StreamInfo) {
defer wg.Done()
mutex.Lock()
defer mutex.Unlock()
Expand All @@ -95,9 +103,9 @@ func mergeStreamInfo(existing, new []StreamInfo) []StreamInfo {
return existing
}

func parseM3UFile(filePath string, m3uIndex int) ([]StreamInfo, error) {
func parseM3UFile(filePath string, m3uIndex int) ([]database.StreamInfo, error) {
fmt.Printf("Parsing: %s\n", filePath)
var streams []StreamInfo
var streams []database.StreamInfo

file, err := os.Open(filePath)
if err != nil {
Expand All @@ -107,13 +115,13 @@ func parseM3UFile(filePath string, m3uIndex int) ([]StreamInfo, error) {

scanner := bufio.NewScanner(file)

var currentStream StreamInfo
var currentStream database.StreamInfo

for scanner.Scan() {
line := scanner.Text()

if strings.HasPrefix(line, "#EXTINF:") {
currentStream = StreamInfo{}
currentStream = database.StreamInfo{}

// Define a regular expression to capture key-value pairs
regex := regexp.MustCompile(`(\S+?)="([^"]*?)"`)
Expand Down Expand Up @@ -144,7 +152,7 @@ func parseM3UFile(filePath string, m3uIndex int) ([]StreamInfo, error) {
}
} else if strings.HasPrefix(line, "http") {
// Extract URL
currentStream.URLs = []StreamURL{
currentStream.URLs = []database.StreamURL{
{
Content: line,
M3UIndex: m3uIndex,
Expand Down
Loading

0 comments on commit e7d93c3

Please sign in to comment.