Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 17 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,12 @@
Subify is a tool to download subtitles for your favorite TV shows and movies.
It is directly able to open the video with your default player, once the subtitle is downloaded.

Subify combines [SubDB Web API](http://thesubdb.com/), [OpenSubtitles API](http://trac.opensubtitles.org/projects/opensubtitles/wiki) and [Addic7ed](http://www.addic7ed.com/) to get the best subtitles for your video. It also considers that you use a default player interpreting srt subtitles when the video file name is the same than the srt file (ex: [VLC](http://www.videolan.org/vlc/)).
Subify combines [OpenSubtitles API](http://trac.opensubtitles.org/projects/opensubtitles/wiki) and [Addic7ed](http://www.addic7ed.com/) to get the best subtitles for your video. It also considers that you use a default player interpreting srt subtitles when the video file name is the same than the srt file (ex: [VLC](http://www.videolan.org/vlc/)).

Subify gets the best match from several APIs in this order. This default behavior can easily be changed. See the documentation below

1. SubDB
2. OpenSubtitles
3. Addic7ed
1. OpenSubtitles
2. Addic7ed

## Installing

Expand Down Expand Up @@ -71,16 +70,16 @@ Issues:
Note : the binary is usable as is. If you want to run the command from anywhere on your OS, make sure to add Subify home installation to your PATH environment variable

```shell
# Download subtitle with default language (English) from default APIs (SubDB, then OpenSubtitles, then Addic7ed)
# Download subtitle with default language (English) from default APIs (OpenSubtitles, then Addic7ed)
subify dl <path_to_your_video>
# Download subtitle with default language (English), from default APIs (SubDB, then OpenSubtitles, then Addic7ed), then open video with your default player
# Download subtitle with default language (English), from default APIs (OpenSubtitles, then Addic7ed), then open video with your default player
subify dl <path_to_your_video> -o
# Download subtitle with french language, from default APIs (SubDB, then OpenSubtitles, then Addic7ed), and open with your default player
# Download subtitle with french language, from default APIs (OpenSubtitles, then Addic7ed), and open with your default player
subify dl <path_to_your_video> -o -l fr
# Download subtitle with french language, if not found spanish, if not found english, from default APIs (SubDB, then OpenSubtitles, then Addic7ed)
# Download subtitle with french language, if not found spanish, if not found english, from default APIs (OpenSubtitles, then Addic7ed)
subify dl <path_to_your_video> -l fr,es,en
# Download subtitle with default language, by searching first in OpenSubtitles, then in SubDB
subify dl <path_to_your_video> -a os,subdb
# Download subtitle with default language, by searching first in Addic7ed then OpenSubtitles
subify dl <path_to_your_video> -a add,os
# Download subtitle with default language, by searching only in OpenSubtitles
subify dl <path_to_your_video> -a OpenSubtitles
```
Expand Down Expand Up @@ -121,16 +120,18 @@ Aliases:
dl, download

Flags:
-a, --apis string Overwrite default searching APIs behavior, hence the subtitles are downloaded. Available APIs at 'subify list apis' (default "SubDB,OpenSubtitles,Addic7ed")
-a, --apis string Overwrite default searching APIs behavior, hence the subtitles are downloaded. Available APIs at 'subify list apis' (default "OpenSubtitles,Addic7ed")
-h, --help help for dl
-l, --languages string Languages of the subtitle separate by a comma (First to match is downloaded). Available languages at 'subify list languages' (default "en")
--lang-in-filename Language is in the filename (e.g. if enabled: 'xxx.English.srt' for English), else 'xxx.srt' (default true)
-l, --languages string Languages of the subtitle separated by a comma (First to match is downloaded). Available languages at 'subify list languages' (default "en")
-n, --notify Display desktop notification (default true)
-o, --open Once the subtitle is downloaded, open the video with your default video player (OSX: "open", Windows: "start", Linux/Other: "xdg-open")

Global Flags:
--config string Config file (default is $HOME/.subify.yaml|json|toml). Edit to change default behavior
--dev Instantiate development sandbox instead of production variables
-v, --verbose Print more information while executing

```

### Listing command
Expand Down Expand Up @@ -188,11 +189,14 @@ dev = false # Don't turn on, just for development purpose
# download for the download/dl command
[download]
languages = "en" # Searching for theses languages. Can be a list like : "fr,es,en"
apis = "SubDB,OpenSubtitles,Addic7ed" # Searching from these sites
apis = "OpenSubtitles,Addic7ed" # Searching from these sites
notify = false
```

## Release Notes
* **0.6.0** Apr 2, 2025
* Implemented #
* Removed Subdb (project is dead)
* **0.5.0** Apr 2, 2025
* Migrate to Go 1.23
* Updated Addic7ed library (again), as they changed implementation of the download button
Expand Down
21 changes: 16 additions & 5 deletions cmd/dl.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,21 @@ package cmd
import (
"strings"

"github.com/matcornic/subify/common/utils"
"github.com/matcornic/subify/subtitles"
"github.com/skratchdot/open-golang/open"
"github.com/spf13/cobra"
logger "github.com/spf13/jwalterweatherman"
"github.com/spf13/viper"

"github.com/matcornic/subify/common/utils"
"github.com/matcornic/subify/subtitles"
)

var openVideo bool

var notify bool

var langInFileName bool

// dlCmd represents the dl command
var dlCmd = &cobra.Command{
Use: "dl <video-path>",
Expand All @@ -31,7 +34,14 @@ Give the path of your video as first parameter and let's go !`,

apis := strings.Split(viper.GetString("download.apis"), ",")
languages := strings.Split(viper.GetString("download.languages"), ",")
err := subtitles.Download(videoPath, apis, languages, notify)
options := []func(*subtitles.DownloadOptions){}
if langInFileName {
options = append(options, subtitles.WithLangInFileName())
}
if notify {
options = append(options, subtitles.WithOSNotification())
}
err := subtitles.Download(videoPath, apis, languages, options...)
if err != nil {
utils.ExitPrintError(err, "Sadly, we could not download any subtitle for you. Try another time or contribute to the apis. See 'subify upload -h'")
}
Expand All @@ -45,12 +55,13 @@ Give the path of your video as first parameter and let's go !`,
}

func init() {
dlCmd.Flags().StringP("languages", "l", "en", "Languages of the subtitle separate by a comma (First to match is downloaded). Available languages at 'subify list languages'")
dlCmd.Flags().StringP("apis", "a", "SubDB,OpenSubtitles,Addic7ed", "Overwrite default searching APIs behavior, hence the subtitles are downloaded. Available APIs at 'subify list apis'")
dlCmd.Flags().StringP("languages", "l", "en", "Languages of the subtitle separated by a comma (First to match is downloaded). Available languages at 'subify list languages'")
dlCmd.Flags().StringP("apis", "a", "OpenSubtitles,Addic7ed", "Overwrite default searching APIs behavior, hence the subtitles are downloaded. Available APIs at 'subify list apis'")
dlCmd.Flags().BoolVarP(&openVideo, "open", "o", false,
"Once the subtitle is downloaded, open the video with your default video player"+
` (OSX: "open", Windows: "start", Linux/Other: "xdg-open")`)
dlCmd.Flags().BoolVarP(&notify, "notify", "n", true, "Display desktop notification")
dlCmd.Flags().BoolVar(&langInFileName, "lang-in-filename", true, "Language is in the filename (e.g. if enabled: 'xxx.English.srt' for English), else 'xxx.srt'")
_ = viper.BindPFlag("download.languages", dlCmd.Flags().Lookup("languages"))
_ = viper.BindPFlag("download.apis", dlCmd.Flags().Lookup("apis"))

Expand Down
3 changes: 0 additions & 3 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@ go 1.23.8

require (
github.com/deckarep/gosx-notifier v0.0.0-20180201035817-e127226297fb
github.com/google/go-querystring v1.1.0
github.com/jacobmarshall/go-toast v0.0.0-20190211030409-01e6764cf0a4
github.com/lafikl/fluent v0.0.0-20141109195914-392b95b3b5b2
github.com/matcornic/addic7ed v0.2.1
github.com/olekukonko/tablewriter v0.0.5
github.com/oz/osdb v0.0.0-20221214175751-f169057712ec
Expand All @@ -26,7 +24,6 @@ require (
github.com/go-viper/mapstructure/v2 v2.2.1 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/kolo/xmlrpc v0.0.0-20220921171641-a4b6fa1dd06b // indirect
github.com/lafikl/backoff v0.0.0-20150814094333-4dc77674acea // indirect
github.com/masatana/go-textdistance v0.0.0-20191005053614-738b0edac985 // indirect
github.com/mattn/go-runewidth v0.0.9 // indirect
github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d // indirect
Expand Down
8 changes: 0 additions & 8 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,8 @@ github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/
github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss=
github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/jacobmarshall/go-toast v0.0.0-20190211030409-01e6764cf0a4 h1:OMNCPYu0cq+d1aM/RrQAcggZySgC7tYltYz4G1C6bLo=
Expand All @@ -31,10 +28,6 @@ github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/lafikl/backoff v0.0.0-20150814094333-4dc77674acea h1:BVM4DiWqlVWFfp9Smd5hr2ntgWjmQ8Y7v+Pa+vVSdQ0=
github.com/lafikl/backoff v0.0.0-20150814094333-4dc77674acea/go.mod h1:6QVfeSMvsSeGKj76ZIg7cxkSvMZgxjGWkBB9iQ+OIVw=
github.com/lafikl/fluent v0.0.0-20141109195914-392b95b3b5b2 h1:eRcUPoD8nHzgmNR/Oqb06uYKSkNa+W7J6NKgmVHC0eY=
github.com/lafikl/fluent v0.0.0-20141109195914-392b95b3b5b2/go.mod h1:XqgOzp3xB8IvokkubmN6YY1ylcNqt1WvRINPyPVc86Q=
github.com/masatana/go-textdistance v0.0.0-20191005053614-738b0edac985 h1:Pz8zZjVRvKxISYimNzLGnzSNl5hYXFSN80FPQ+qt1HE=
github.com/masatana/go-textdistance v0.0.0-20191005053614-738b0edac985/go.mod h1:1nU7rI+iBPtzc9ZKOqeQacD290rA0wcJLu5AtOSBBPw=
github.com/matcornic/addic7ed v0.2.1 h1:44bNquok3pls5vpwcaeUxz/ZaMxpqnxEX7AxmFRmQZY=
Expand Down Expand Up @@ -153,7 +146,6 @@ golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
Expand Down
14 changes: 11 additions & 3 deletions subtitles/addic7ed.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,9 +80,12 @@ func Addic7ed() Addic7edAPI {
}

// Download downloads the Addic7ed subtitle from a video
func (s Addic7edAPI) Download(videoPath string, language Language) (subtitlePath string, err error) {
func (s Addic7edAPI) Download(videoPath string, language Language, downloadOptions ...func(*DownloadOptions)) (subtitlePath string, err error) {
c := addic7ed.New()

options := &DownloadOptions{}
for _, o := range downloadOptions {
o(options)
}
lang, ok := addic7edLangs[language.ID]
if !ok {
return "", errors.New("Language exists but is not available for Addic7ed")
Expand All @@ -94,7 +97,12 @@ func (s Addic7edAPI) Download(videoPath string, language Language) (subtitlePath
}

// Saving to disk
subtitlePath = videoPath[0:len(videoPath)-len(path.Ext(videoPath))] + "." + lang + ".srt"
basePath := videoPath[0 : len(videoPath)-len(path.Ext(videoPath))]
if options.langInFileName {
subtitlePath = basePath + "." + lang + ".srt"
} else {
subtitlePath = basePath + ".srt"
}
if err := subtitle.DownloadTo(subtitlePath); err != nil {
return "", err
}
Expand Down
8 changes: 4 additions & 4 deletions subtitles/languages.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,11 +77,11 @@ func (l Langs) Print(all bool) {
sort.Sort(ByName(langs))

for _, l := range langs {
_, subOK := subdbLangs[l.ID]
_, addOK := addic7edLangs[l.ID]
_, osOK := osLangs[l.ID]
if all {
available := "No"
if subOK || osOK {
if addOK || osOK {
available = "Yes"
}
values := []string{
Expand All @@ -90,7 +90,7 @@ func (l Langs) Print(all bool) {
available, // Available ?
}
table.Append(values)
} else if subOK || osOK {
} else if addOK || osOK {
values := []string{
l.Description, // Language
strings.Join(append(l.Alias, l.ID), ", "), // Available Id(s)
Expand All @@ -103,7 +103,7 @@ func (l Langs) Print(all bool) {
table.Render() // Send output
}

//Languages is the list of all languages
// Languages is the list of all languages
var Languages = Langs{
{"aar", []string{"aa"}, "Afar, afar"},
{"abk", []string{"ab"}, "Abkhazian"},
Expand Down
13 changes: 11 additions & 2 deletions subtitles/opensubtitles.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,13 +91,17 @@ func OpenSubtitles() OSDBAPI {
}

// Download downloads the OpenSubtitles subtitle from a video
func (s OSDBAPI) Download(videoPath string, language Language) (subtitlePath string, err error) {
func (s OSDBAPI) Download(videoPath string, language Language, downloadOptions ...func(*DownloadOptions)) (subtitlePath string, err error) {
c, err := osdb.NewClient()
if err != nil {
return "", err
}
c.UserAgent = osdbUserAgent

options := &DownloadOptions{}
for _, o := range downloadOptions {
o(options)
}
// Anonymous login
if err = c.LogIn("", "", ""); err != nil {
return "", err
Expand All @@ -121,7 +125,12 @@ func (s OSDBAPI) Download(videoPath string, language Language) (subtitlePath str
}

// Saving to disk
subtitlePath = videoPath[0:len(videoPath)-len(path.Ext(videoPath))] + "." + lang + ".srt"
basePath := videoPath[0 : len(videoPath)-len(path.Ext(videoPath))]
if options.langInFileName {
subtitlePath = basePath + "." + lang + ".srt"
} else {
subtitlePath = basePath + ".srt"
}
if err := c.DownloadTo(best, subtitlePath); err != nil {
return "", err
}
Expand Down
Loading
Loading