Skip to content

Commit

Permalink
Merge branch 'release/v1.2.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
slaveofcode committed May 6, 2020
2 parents 595e6fe + c268a14 commit 1bab3fe
Show file tree
Hide file tree
Showing 28 changed files with 703 additions and 88 deletions.
46 changes: 46 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# Voodio Change Log
All notable changes to this project will be documented in this file.

## [1.2.0]
- 105adde Change Readme
- 9a862e0 Update web static to support subtitles
- 1250952 Merge branch 'feature/add-subtitle-video' into develop
- f580a2d Removed comments
- 0ae248d Added subtitles to json responses & parse subtitles - on web
- e1ab89a Scan, detect & save .srt file info to DB
- 659408d Fixed goroutines HLS extraction error & change - logging format
- b3c010f Fix wrong env on build
- 577a284 Update static web
- 539809e Merge branch 'feature/handle-unrecognized-movie' into - develop
- 25f6232 Support single unknown movie
- f5a1247 Handle unrecognized movie & group movie
- 903c3a9 Added go-sqlite3 runtime
- d11f271 Merge branch 'master' of github.com:slaveofcode/- voodio into develop
- 060ef6c Update README.md
- f5984f5 Merge tag 'v1.1.1' into develop
- 4ae2571 Merge branch 'release/v1.1.1'
- 27ad020 Added logo
- 205ddfc Merge tag 'v1.1.0' into develop

## [v1.1.1]

## [v1.1.0]
- c6d232b Merge branch 'release/v1.1.0'
- 873e945 Merge branch 'master' of github.com:slaveofcode/- voodio into release/v1.1.0
- 9654e9c Update readme
- e1c12b0 Update readme
- 61c443b Replace screenshot
- a1cab02 Remove unused README
- 81479b5 Added goreleaser config
- ab7dcea Fix padding layout footer
- f079056 Merge branch 'feature/add-tmdb-copyright' into develop
- 579a593 Configure TMDB options for getting movie information
- 03672bd Configure logo, TMDB logo and dynamic tmdb api key
- b341590 Merge branch 'feature/add-opts-path' into develop
- 28f2adc Removes extra UI server, combine with existing routes
- f28c586 Added custom ffmpeg path
- 6ad6c8e Update README.md
- f233643 Update README.md
- 4d48886 Update README.md
- 0df288d Update README.md
- 028631b Merge tag 'v1.0.0' into develop
12 changes: 7 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,19 +1,21 @@
<img src="https://raw.github.com/slaveofcode/voodio/master/assets/Voodio.png" align="right" />

# Voodio (Private Media Server)
Voodio is a Simple Private Media Server based on your local Movie Directories. It just a simple program doing tracking on your "Movies" folder, create an index and showing the movies as a web UI to play streamly through the browser.
Voodio is a Simple Private Media Server based on your local Movie Directories. It just a simple program doing tracking on your "Movies" folder, create an index and showing the movies via Web UI to play streamly through the browser.

## Background
I always wanted to watch my old movie collection that saved on my external hardisk or on my PC drive. But unfortunately I'am too lazy to open my computer and starting to crawl and watch those movies. I always wonder if I could see those movies as a webpage, click the detail and play, like I'm watching on the Netfl*x, but the movie is based on my collection.
I always wanted to watch my old movie collection that saved on my external hardisk or on my PC drive. But unfortunately I'am too lazy to open my computer, starting to crawl and watch those movies. I always wonder if I could see those movies as a Website, click the detail and play, like I'm watching on the Netfl*x, but the movie is on local Hard Drive.

## Application Behavior
This application will need extra space like **8-10x** of the played video which extracted from FFmpeg transcoding/transmuxing process of **HLS** files, so then you can play it streamly through your favourite device via **Browser**. The space will be cleand up after the server is turned of (killed), it will be immediatelly deletes all the generated HLS files so you can get the disk again at the end.
This application will need extra space like **8-10x** of the played video which extracted from FFmpeg transcoding/transmuxing process of **HLS** files, the space needed could be lower if there a less available resolution generated (like only for 480p or 720o), then you can play it streamly through your local network on your favourite device via **Browser**. The space will be cleand up after the server is turned of (killed), it will be immediatelly deletes all the generated HLS files so you get the space again.

## Installation

### Have FFMPEG installed on your OS

Visit [FFmpeg Official Download](https://www.ffmpeg.org/download.html) page to install based on your current OS. FFmpeg is available for **Windows**, **Mac** and **Linux**
Visit [FFmpeg Official Download](https://www.ffmpeg.org/download.html) page to install based on your current OS. FFmpeg is available for **Windows**, **Mac** and **Linux**. Please use FFmpeg with version below **4.0** or better with version **3.***.

FFmpeg with version >= 4.1 had an issue of immediatelly generate playlist file (**.m3u8**), then do update periodically at transcoding time, I don't know why that's happen, but I think there's a solution for that, just need a more time to find out.

### Using Precompiled Binary

Expand All @@ -38,7 +40,7 @@ If the configuration and steps above is complete, you can heads up to http://[yo
- `-ffmpeg-bin` (optional) The path of FFmpeg binary, if you have a different path of FFmpeg

### Screenshot
<img src="https://raw.github.com/slaveofcode/voodio/master/assets/home.jpg" align="center" />
<img src="https://raw.github.com/slaveofcode/voodio/master/assets/home.png" align="center" />
<img src="https://raw.github.com/slaveofcode/voodio/master/assets/detail.png" align="center" />
<img src="https://raw.github.com/slaveofcode/voodio/master/assets/play.png" align="center" />

Expand Down
Binary file modified assets/detail.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file removed assets/home.jpg
Binary file not shown.
Binary file added assets/home.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified assets/play.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
70 changes: 51 additions & 19 deletions collections/extract_hls.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
package collections

import (
"log"
"os"
"os/exec"
"path/filepath"
"strconv"
"strings"

"github.com/sirupsen/logrus"
"github.com/slaveofcode/voodio/repository/models"
)

Expand All @@ -15,6 +16,12 @@ const (
ExtractionDirName = "generated_hls"
)

// TranscodingError will hold the error information after transcoding process finished
type TranscodingError struct {
Resolution string
Error error
}

func cmdHLS360p(movieFilePath, destDir string) []string {
// fixed width not divisible by 2
// see https://superuser.com/questions/624563/how-to-resize-a-video-to-make-it-smaller-with-ffmpeg
Expand Down Expand Up @@ -140,7 +147,7 @@ func createm3u8Playlist(path string) {
}

// ExtractMovHLS will generate HLS files
func ExtractMovHLS(movieFilePath, destDir, ffmpegPathBin string) error {
func ExtractMovHLS(movieFilePath, destDir, ffmpegPathBin string) (bool, []TranscodingError) {
resolutions := map[string]func(string, string) []string{
"360p": cmdHLS360p,
"480p": cmdHLS480p,
Expand All @@ -150,41 +157,66 @@ func ExtractMovHLS(movieFilePath, destDir, ffmpegPathBin string) error {

createm3u8Playlist(destDir)

output := make(chan error, len(resolutions))
for _, cmdStrings := range resolutions {
go func(out chan<- error, commandProducer func(string, string) []string) {
output := make(chan TranscodingError, len(resolutions))
for reso, cmdStrings := range resolutions {
go func(out chan<- TranscodingError, commandProducer func(string, string) []string, resolution string) {
cmd := exec.Command(ffmpegPathBin, commandProducer(movieFilePath, destDir)...)
stdout, err := cmd.CombinedOutput()
cmd.Stdout = logrus.New().Out
cmd.Stderr = logrus.New().Out

logrus.Infoln("Exec:", strings.Join(cmd.Args, " "))

err := cmd.Start()

log.Println("Args:", strings.Join(cmd.Args, " "))
log.Println("output:", string(stdout))
if err != nil {
out <- err
logrus.Errorln("Exec error:", err)
out <- TranscodingError{
Resolution: resolution,
Error: err,
}
return
}

out <- nil
}(output, cmdStrings)
out <- TranscodingError{
Resolution: resolution,
Error: nil,
}
}(output, cmdStrings, reso)
}

var errors []error
var hasError bool
errors := make([]TranscodingError, len(resolutions))

iter := 0
for out := range output {
if out.Error != nil && !hasError {
hasError = true
}
errors = append(errors, out)
iter++

if iter == len(resolutions) {
close(output)
}
}

log.Println(errors)
return hasError, errors
}

close(output)
func getExtractionMovieDir(appDir string, movieID uint) string {
return filepath.Join(appDir, ExtractionDirName, strconv.Itoa(int(movieID)))
}

return nil
func createWriteableDir(path string) error {
return os.MkdirAll(path, 0777)
}

// DoExtraction will do extract HLS files
func DoExtraction(movie *models.Movie, appDir string, FFmpegBin string) error {
extractionDirName := filepath.Join(appDir, ExtractionDirName, movie.CleanDirName)
func DoExtraction(movie *models.Movie, appDir string, FFmpegBin string) (bool, []TranscodingError) {
extractionDirName := getExtractionMovieDir(appDir, movie.ID)

if err := os.MkdirAll(extractionDirName, 0777); err != nil {
return err
if err := createWriteableDir(extractionDirName); err != nil {
return true, nil
}

return ExtractMovHLS(
Expand Down
44 changes: 44 additions & 0 deletions collections/process_srt.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package collections

import (
"path/filepath"

"strings"

"github.com/asticode/go-astisub"
"github.com/jinzhu/gorm"
"github.com/sirupsen/logrus"
"github.com/slaveofcode/voodio/repository/models"
)

// ProcessSrt will detect .srt files on DB and convert it into .vtt to extracted movie dir
func ProcessSrt(db *gorm.DB, movie *models.Movie, appDir string) error {
destPath := filepath.Join(getExtractionMovieDir(appDir, movie.ID), "subs")

if err := createWriteableDir(destPath); err != nil {
return err
}

// get subs based on path movie
var subs []models.Subtitle
db.Where(&models.Subtitle{
DirPath: movie.DirPath,
}).Find(&subs)

for _, sub := range subs {
// convert & save to app dir
s, err := astisub.OpenFile(filepath.Join(sub.DirPath, sub.BaseName))
if err != nil {
logrus.Errorln("Couldn't read the SRT file", sub.BaseName, err)
}

s.Write(filepath.Join(destPath, GetVTTFileName(sub.BaseName)))
}

return nil
}

// GetVTTFileName will return .vtt file based on given file name with ext
func GetVTTFileName(srtFileName string) string {
return strings.Split(srtFileName, ".")[0] + ".vtt"
}
57 changes: 46 additions & 11 deletions collections/scanner.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import (
"github.com/gabriel-vasile/mimetype"
)

// MovieDirInfo keep movie directory information
// MovieDirInfo is a holder to keep movie directory information
type MovieDirInfo struct {
Dir string
MovieFile string
Expand All @@ -18,36 +18,50 @@ type MovieDirInfo struct {
MimeType string
}

// SubDirInfo is a holder to subtitle information
type SubDirInfo struct {
Dir string
SubFile string
Info os.FileInfo
}

// ScanDir will return flat list of Movie directory information
func ScanDir(path string) ([]MovieDirInfo, error) {
func ScanDir(path string) ([]MovieDirInfo, []SubDirInfo, error) {
var listMovie []MovieDirInfo
var listSub []SubDirInfo

listItems, err := ioutil.ReadDir(path)
if err != nil {
return nil, err
return nil, nil, err
}

for _, item := range listItems {
detectedMovies, err := Identify(path, item)
detectedMovies, detectedSubs, err := Identify(path, item)
if err != nil {
return nil, err
return nil, nil, err
}

listMovie = append(listMovie, detectedMovies...)
listSub = append(listSub, detectedSubs...)
}

return listMovie, nil
return listMovie, listSub, nil
}

// Identify single directory of movie
func Identify(basePath string, file os.FileInfo) ([]MovieDirInfo, error) {
func Identify(basePath string, file os.FileInfo) ([]MovieDirInfo, []SubDirInfo, error) {
path := filepath.Join(basePath, file.Name())

var listMovie []MovieDirInfo
var listSub []SubDirInfo

if file.IsDir() {
// recursive call, create other segment to continue the scan operation
listMovie, err := ScanDir(filepath.Join(basePath, file.Name()))
return listMovie, err
listMovie, listSub, err := ScanDir(path)
return listMovie, listSub, err
}

if mime, validVideo := isVideo(filepath.Join(basePath, file.Name())); validVideo {
if mime, validVideo := isVideo(path); validVideo {
listMovie = append(listMovie, MovieDirInfo{
Dir: basePath,
MovieFile: file.Name(),
Expand All @@ -57,7 +71,15 @@ func Identify(basePath string, file os.FileInfo) ([]MovieDirInfo, error) {
})
}

return listMovie, nil
if validSub := IsTextSrt(path); validSub {
listSub = append(listSub, SubDirInfo{
Dir: basePath,
SubFile: file.Name(),
Info: file,
})
}

return listMovie, listSub, nil
}

func getFileSizeInMB(file os.FileInfo) float64 {
Expand Down Expand Up @@ -96,3 +118,16 @@ func isVideo(path string) (string, bool) {

return mime.String(), listChecker[mime.String()]
}

// IsTextSrt will return valid status detection of .srt file
func IsTextSrt(path string) bool {
mime, err := mimetype.DetectFile(path)
if err != nil {
return false
}

validMime := mime.String() == "text/plain; charset=utf-8"
validExt := filepath.Ext(path) == ".srt" || filepath.Ext(path) == ".SRT"

return validMime && validExt
}
5 changes: 3 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,14 @@ module github.com/slaveofcode/voodio
go 1.13

require (
github.com/asticode/go-astikit v0.5.0 // indirect
github.com/asticode/go-astisub v0.2.0
github.com/gabriel-vasile/mimetype v1.0.5
github.com/jinzhu/gorm v1.9.12
github.com/konsorten/go-windows-terminal-sequences v1.0.2 // indirect
github.com/mattn/go-sqlite3 v2.0.3+incompatible // indirect
github.com/mattn/go-sqlite3 v2.0.3+incompatible
github.com/middelink/go-parse-torrent-name v0.0.0-20190301154245-3ff4efacd4c4
github.com/rakyll/statik v0.1.7
github.com/rs/cors v1.7.0 // indirect
github.com/sirupsen/logrus v1.5.0
golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f // indirect
)
Loading

0 comments on commit 1bab3fe

Please sign in to comment.