Skip to content

Commit

Permalink
Merge branch 'release/v1.0.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
Amir Mofasser committed Dec 5, 2015
2 parents fdee7f9 + 50b9649 commit 29e4c28
Show file tree
Hide file tree
Showing 5 changed files with 678 additions and 1 deletion.
28 changes: 27 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,28 @@
# video-scanner-go
Scans a directory for video content and organizes them at a target directory
A utility written in Go that copies your movies and series from source to target directory.
Scans a directory for video content and organizes them at a target directory. It is capable of
detecting movies and series based on their file name.

# Usage
Search the target (-t) directory ~/Downloads for media. Copies any movies (-m) to ~/Media/Movies and (-s) series to ~/Media/Series.
Below example will overwrite any already existing files and folders (-o)
```
./scanner -m ~/Media/Movies -s ~/Media/Series -t ~/Downloads -o
```

Other available command line options are
```
Usage of ./scanner:
-c Prompt for confirm when overwriting existing files/folders
-m string
Directory to your movies. (default ".")
-o Overwrite existing files/folders when copying
-s string
Directory to your series. (default ".")
-t string
Target directory. Typically your Downloads folder. (default ".")
-u string
String representation of unit to use when calculating file sizes. Choices are k, m, g and t (default "g")
-v int
Log level. 3=DEBUG, 2=WARN, 1=INFO, 0=DEBUG. (default "0")
```
Binary file added bin/scanner
Binary file not shown.
174 changes: 174 additions & 0 deletions src/github.com/amimof/copy/copy.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
package copy

/**
*
* Author: Amir Mofasser <amir.mofasser@gmail.com>
* https://github.com/amimof
*
*/

import (
"github.com/amimof/logger"
"github.com/cheggaaa/pb"
"io/ioutil"
"io"
"path"
"os"
"time"
)

var log *logger.Logger = logger.SetupNew("COPY")

// Copies a dir tree from src to dst. Overwrites content if ow is true
func CopyDir(src, dst string, ow bool) (int64, error) {

// Stat source
s, err := os.Stat(src)
if err != nil {
log.Error("Couldn't stat", src, err)
return 0, err
}

// Ensure that src is a dir
if !s.IsDir() {
log.Error("Source is not a directory")
return 0, err
}

// Ensure that dst does not already exist
d, err := os.Open(dst)
if err != nil {
log.Error("Couldn't open", dst, err)
}
if ow == false {
if !os.IsNotExist(err) {
return 0, err
}
}
defer d.Close()

// Create dest dir
err = os.MkdirAll(dst, s.Mode())
if err != nil {
log.Error("Couldn't create", dst, err)
return 0, err
}

var written int64
entries, err := ioutil.ReadDir(src)
if err != nil {
log.Error("Couldn't read", src, err)
return 0, err
} else {
for _, entry := range entries {

sfp := path.Join(src, entry.Name())
dfp := path.Join(dst, entry.Name())

if entry.IsDir() {
written, err = CopyDir(sfp, dfp, ow)
if err != nil {
log.Error("Couldn't copy", err)
return 0, err
}
} else {
written, err = CopyFile(sfp, dfp, ow)
if err != nil {
log.Error("Couldn't copy", err)
return 0, nil
}
}
}
}

return written, nil

}

// Copies the content of src file to dst. Overwrites dst file if ow os true
func CopyFile(src, dst string, ow bool) (int64, error) {

// Create source
var source io.Reader
s, err := os.Open(src)
if err != nil {
log.Error("Couldn't open", src, err)
return 0, err
}
defer s.Close()

// Stat source
srcStat, err := s.Stat()
if err != nil {
log.Error("Couldn't stat", src, err)
return 0, err
}
sourceSize := srcStat.Size()
source = s

// Check if dst exists
d, err := os.Open(dst)
if !os.IsNotExist(err) {
if ow == false {
return 0, err
}
}
defer d.Close()

// Create dest
dest, err := os.Create(dst)
if err != nil {
log.Error("Couldn't create", dst, err)
return 0, err
}
defer dest.Close()

// Create the progress bar
bar := pb.New(int(sourceSize)).SetUnits(pb.U_BYTES).SetRefreshRate(time.Millisecond*10).Prefix(truncate(path.Base(src), 10, true)+": ")
bar.Format("<.->")
bar.ShowSpeed = true
bar.Start()

// Copy
writer := io.MultiWriter(dest, bar)
written, err := io.Copy(writer, source)
if err != nil {
log.Error("Couldn't copy", err)
return 0, err
}

bar.Finish()
return written, nil
}


// Truncates string to the length specified with len.
// if bo is true, then truncated text will be represented by . chars
func truncate(str string, length int, bo bool) string {
var cut int
var newStr string
var appendStr string
// Dont cut more than the max number of chars in the string
// Will throw 'slice bounds out of range' otherwise
strLen := len([]rune(str))
if length >= strLen {
cut = strLen
} else {
cut = length
}

newStr = str[:cut]

if bo == true {
appendStr = str[(strLen-3):strLen]
}

returnStr := newStr + "..." + appendStr

// If the new string is longer than the originial, the just return the originial string
if returnStr >= str {
returnStr = str
}

return returnStr
}
175 changes: 175 additions & 0 deletions src/github.com/amimof/logger/logger.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
package logger

import (
"fmt"
"io"
"sync"
"os"
)

// Define colors
const (
CLR_0 = "\x1b[30;1m"
CLR_R = "\x1b[31;1m"
CLR_G = "\x1b[32;1m"
CLR_Y = "\x1b[33;1m"
CLR_B = "\x1b[34;1m"
CLR_M = "\x1b[35;1m"
CLR_C = "\x1b[36;1m"
CLR_W = "\x1b[37;1m"
CLR_N = "\x1b[0m"
DEBUG = 3
INFO = 2
WARN = 1
ERROR = 0
)

var (
std = NewStdLogger(os.Stderr)
new *Logger
)

type StandardLogger struct {
mu sync.Mutex
out io.Writer
buf []byte
}

type Logger struct {
name string
Level *Level
}

type Level struct {
num int
name string
}

func NewStdLogger(out io.Writer) *StandardLogger {
return &StandardLogger{out: out}
}

func (l *Logger) New(name string, level *Level) *Logger {
l.name = name
l.Level = level
return l
}

func (l *Level) SetLevel(level int) *Level {
switch level {
case 0:
l.num = 0
l.name = "ERROR"
case 1:
l.num = 1
l.name = "WARN"
case 2:
l.num = 2
l.name = "INFO"
case 3:
l.num = 3
l.name = "DEBUG"
default:
l.num = 1
l.name = "INFO"
}
return l
}

// Writes the specified string to std and colors it accordingly
func (l *StandardLogger) Output(color, str string) error {
l.mu.Lock()
lgr := getLogger()
defer l.mu.Unlock()
l.buf = l.buf[:0]
l.buf = append(l.buf, color...)
l.buf = append(l.buf, lgr.Level.name...)
l.buf = append(l.buf, ' ')
l.buf = append(l.buf, lgr.name...)
l.buf = append(l.buf, ' ')
l.buf = append(l.buf, str...)
l.buf = append(l.buf, CLR_N...)
_, err := l.out.Write(l.buf)
return err
}
// Return the level
func (l *Logger) GetLevel() *Level {
return l.Level
}

// Prints according to fmt.Sprintf format specifier and returns the resulting string
func (l *Logger) Debugf(format string, message ...interface{}) {
if l.Level.num >= DEBUG {
std.Output(CLR_G, fmt.Sprintf(format, message...))
}
}

// Prints a debug message on a new line
func (l *Logger) Debug(message ...interface{}) {
if l.Level.num >= DEBUG {
std.Output(CLR_G, fmt.Sprintln(message...))
}
}

// Prints according to fmt.Sprintf format specifier and returns the resulting string
func (l *Logger) Infof(format string, message ...interface{}) {
if l.Level.num >= INFO {
std.Output(CLR_W, fmt.Sprintf(format, message...))
}
}

// Prints n info message on a new line
func (l *Logger) Info(message ...interface{}) {
if l.Level.num >= INFO {
std.Output(CLR_W, fmt.Sprintln(message...))
}
}

// Prints according to fmt.Sprintf format specifier and returns the resulting string
func (l *Logger) Warnf(format string, message ...interface{}) {
if l.Level.num >= WARN {
std.Output(CLR_Y, fmt.Sprintf(format, message...))
}
}

// Prints a warning message on a new line
func (l *Logger) Warn(message ...interface{}) {
if l.Level.num >= WARN {
std.Output(CLR_Y, fmt.Sprintln(message...))
}
}

// Prints according to fmt.Sprintf format specifier and returns the resulting string
func (l *Logger) Errorf(format string, message ...interface{}) {
if l.Level.num >= ERROR {
std.Output(CLR_R, fmt.Sprintf(format, message...))
}
}

// Prints an error message on a new line
func (l *Logger) Error(message ...interface{}) {
if l.Level.num >= ERROR {
std.Output(CLR_R, fmt.Sprintln(message...))
}
}

// Return logger
func getLogger() *Logger {
return new
}

// Set the standard logger output
func SetOutput(out io.Writer) {
std.mu.Lock()
defer std.mu.Unlock()
std.out = out
}

// Setup and return logger
func SetupNew(name string) *Logger {
new = &Logger{
name: name,
Level: &Level{1, "INFO"},
}
return new
}

0 comments on commit 29e4c28

Please sign in to comment.