-
Notifications
You must be signed in to change notification settings - Fork 0
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
Showing
5 changed files
with
678 additions
and
1 deletion.
There are no files selected for viewing
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 |
---|---|---|
@@ -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 not shown.
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,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 | ||
} |
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,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 | ||
} |
Oops, something went wrong.