Clean up everything
This commit is contained in:
parent
fd853b559e
commit
4928ee1c6e
|
@ -1,2 +1 @@
|
|||
# Include the license file
|
||||
include LICENSE README.rst
|
||||
include LICENSE README.rst doc/screenshot.png
|
||||
|
|
47
README.rst
47
README.rst
|
@ -1,36 +1,39 @@
|
|||
=================================
|
||||
comp - Curses Online Media Player
|
||||
=================================
|
||||
===============================
|
||||
comp - Curses Omni Media Player
|
||||
===============================
|
||||
|
||||
This program is a curses front-end for mpv and youtube-dl.
|
||||
|
||||
.. image:: https://ipfs.io/ipfs/QmVhz4F53Sym48kXC7vhDMFsfvJ7iL8gaQ1EgoQADJvuAB
|
||||
.. image:: doc/screenshot.png
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
Dependencies
|
||||
^^^^^^^^^^^^
|
||||
comp requires Python 3.5+ with ``curses`` module (only available on Unix-like
|
||||
OSes such as GNU/Linux and the BSDs) and ``libmpv``. It also depends on
|
||||
``python-mpv`` and ``youtube-dl`` but the setup program will automatically
|
||||
install them if they are missing.
|
||||
|
||||
This program currently only runs on Python 3.5+ on operating systems that the
|
||||
``curses`` module is supported (i.e. Unix-like OS, e.g. GNU/Linux, macOS and
|
||||
the BSDs).
|
||||
Using pip
|
||||
^^^^^^^^^
|
||||
|
||||
It also depends on ``youtube-dl`` and ``libmpv``. Both of those should be
|
||||
available in your operating system's repository, although it's more
|
||||
recommended to install ``youtube-dl`` using ``pip`` (currently most distros
|
||||
still use Python 2 as default so the command is something like ``pip3 install
|
||||
youtube-dl``).
|
||||
Python 2 is still the default on most distributions so the command would be
|
||||
``pip3 install comp``. You can use the ``--user`` flag to avoid system-wide
|
||||
installation.
|
||||
|
||||
Installing comp
|
||||
^^^^^^^^^^^^^^^
|
||||
Using setup.py
|
||||
^^^^^^^^^^^^^^
|
||||
|
||||
I will try to upload the program to PyPI when it's more completed but as of
|
||||
this moment, I'd suggest you to use ``git`` to get the software::
|
||||
To install the latest version or test the development branch (called
|
||||
``bachelor``, in contrast to ``master``), you'll need to do it manually::
|
||||
|
||||
git clone https://github.com/McSinyx/comp.git
|
||||
cd comp
|
||||
sudo ./setup.py install
|
||||
git checkout bachelor # usually master is synced with the PyPI repo
|
||||
sudo ./setup.py install -e .
|
||||
|
||||
Note ``setup.py`` uses ``setuptools`` which is a third-party module and can be
|
||||
install using ``pip3``.
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
@ -88,6 +91,8 @@ Keyboard control
|
|||
+--------------+---------------------------------------------+
|
||||
| ``d`` | Delete current entry |
|
||||
+--------------+---------------------------------------------+
|
||||
| ``i`` | Insert playlist |
|
||||
+--------------+---------------------------------------------+
|
||||
| ``m``, ``M`` | Cycle through playing modes |
|
||||
+--------------+---------------------------------------------+
|
||||
| ``n`` | Repeat previous search |
|
||||
|
@ -120,8 +125,8 @@ Keyboard control
|
|||
Configuration files
|
||||
-------------------
|
||||
|
||||
The system-wide configuration file is ``/etc/comp/settings.ini``, the
|
||||
user-specific one is ``~/.config/mpv/settings.ini``. Default configurations
|
||||
If not specified by the ``--config``, (user-specific) configuration file is
|
||||
``~/.config/mpv/settings.ini``. Default configurations
|
||||
are listed below::
|
||||
|
||||
[comp]
|
||||
|
|
87
comp
87
comp
|
@ -1,6 +1,5 @@
|
|||
#!/usr/bin/env python3
|
||||
#
|
||||
# comp - Curses Online Media Player
|
||||
# comp - Curses Omni Media Player
|
||||
#
|
||||
# comp is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
|
@ -24,18 +23,20 @@ from argparse import ArgumentParser
|
|||
from collections import deque
|
||||
from configparser import ConfigParser
|
||||
from functools import reduce
|
||||
from gettext import gettext as _, textdomain
|
||||
from gettext import bindtextdomain, gettext as _, textdomain
|
||||
from os import makedirs
|
||||
from os.path import abspath, dirname, expanduser, isfile
|
||||
from threading import Thread
|
||||
|
||||
from youtube_dl import YoutubeDL
|
||||
from mpv import MPV
|
||||
from pkg_resources import resource_filename
|
||||
from youtube_dl import YoutubeDL
|
||||
|
||||
from omp import extract_info, Omp
|
||||
|
||||
# Init gettext
|
||||
textdomain('comp')
|
||||
bindtextdomain('omp', resource_filename('omp', 'locale'))
|
||||
textdomain('omp')
|
||||
|
||||
# Global constants
|
||||
SYSTEM_CONFIG = '/etc/comp/settings.ini'
|
||||
|
@ -92,6 +93,8 @@ class Comp(Omp):
|
|||
' ' if self.vid == 'no' else 'V')
|
||||
adds(right.rjust(curses.COLS), curses.color_pair(12))
|
||||
try:
|
||||
self.played[self.playing]['duration'] = self.mp.osd.duration
|
||||
self.print(self.played[self.playing])
|
||||
left = ' {} / {} {} '.format(
|
||||
self.mp.osd.time_pos, self.mp.osd.duration,
|
||||
'|' if self.mp.pause else '>')
|
||||
|
@ -151,7 +154,7 @@ class Comp(Omp):
|
|||
def _writeln(self, y, title, duration, attr):
|
||||
title_len = curses.COLS-DURATION_COL_LEN-3
|
||||
title = justified(title, title_len)
|
||||
duration = duration.ljust(DURATION_COL_LEN)
|
||||
duration = (duration or '00:00:00').ljust(DURATION_COL_LEN)
|
||||
self.scr.addstr(y, 0, ' {} {} '.format(title, duration), attr)
|
||||
self.scr.refresh()
|
||||
|
||||
|
@ -188,7 +191,7 @@ class Comp(Omp):
|
|||
|
||||
def __init__(self, entries, json_file, mode, mpv_vid, mpv_vo, ytdlf):
|
||||
Omp.__init__(self, entries, lambda name, val: self.update_status(),
|
||||
json_file, mode, mpv_vo, mpv_vid, ytdlf)
|
||||
json_file, mode, mpv_vid, mpv_vo, ytdlf)
|
||||
curses.noecho()
|
||||
curses.cbreak()
|
||||
self.scr.keypad(True)
|
||||
|
@ -319,7 +322,7 @@ parser.add_argument('-e', '--extractor', default='youtube-dl',
|
|||
choices=('json', 'mpv', 'youtube-dl'), required=False,
|
||||
help=_("playlist extractor, default is youtube-dl"))
|
||||
parser.add_argument('file', help=_("path or URL to the playlist to be opened"))
|
||||
parser.add_argument('-c', '--config', required=False,
|
||||
parser.add_argument('-c', '--config', default=USER_CONFIG, required=False,
|
||||
help=_("path to the configuration file"))
|
||||
parser.add_argument('--vid', required=False,
|
||||
help=_("initial video channel. auto selects the default,\
|
||||
|
@ -333,32 +336,12 @@ parser.add_argument('-f', '--format', required=False, metavar='YTDL_FORMAT',
|
|||
args = parser.parse_args()
|
||||
entries = extract_info(args.file, args.extractor)
|
||||
json_file = args.file if args.extractor == 'json' else ''
|
||||
|
||||
config = ConfigParser()
|
||||
if args.config is not None and isfile(args.config):
|
||||
config_file = args.config
|
||||
elif isfile(USER_CONFIG):
|
||||
config_file = USER_CONFIG
|
||||
else:
|
||||
config_file = SYSTEM_CONFIG
|
||||
config.read(config_file)
|
||||
|
||||
if args.vid is not None:
|
||||
vid = args.vid
|
||||
else:
|
||||
vid = config.get('mpv', 'video', fallback='auto')
|
||||
|
||||
if args.vo is not None:
|
||||
vo = args.vo
|
||||
else:
|
||||
vo = config.get('mpv', 'video-output', fallback=None)
|
||||
|
||||
config.read(args.config)
|
||||
vid = args.vid or config.get('mpv', 'video', fallback='auto')
|
||||
vo = args.vo or config.get('mpv', 'video-output', fallback=None)
|
||||
mode = config.get('comp', 'play-mode', fallback='play-current')
|
||||
|
||||
if args.format is not None:
|
||||
ytdlf = args.format
|
||||
else:
|
||||
ytdlf = config.get('youtube-dl', 'format', fallback='best')
|
||||
ytdlf = args.format or config.get('youtube-dl', 'format', fallback='best')
|
||||
|
||||
with Comp(entries, json_file, mode, vid, vo, ytdlf) as comp:
|
||||
c = comp.scr.getch()
|
||||
|
@ -399,7 +382,9 @@ with Comp(entries, json_file, mode, vid, vo, ytdlf) as comp:
|
|||
if s: comp.json_file = s
|
||||
try:
|
||||
makedirs(dirname(abspath(comp.json_file)), exist_ok=True)
|
||||
with open(comp.json_file, 'w') as f: json.dump(comp.entries, f)
|
||||
with open(comp.json_file, 'w') as f:
|
||||
json.dump(comp.entries, f, ensure_ascii=False,
|
||||
indent=2, sort_keys=True)
|
||||
except:
|
||||
errmsg = _("'{}': Can't open file for writing").format(
|
||||
comp.json_file)
|
||||
|
@ -407,14 +392,25 @@ with Comp(entries, json_file, mode, vid, vo, ytdlf) as comp:
|
|||
else:
|
||||
comp.update_status(_("'{}' written").format(comp.json_file))
|
||||
elif c == 100: # letter d
|
||||
i = comp.idx()
|
||||
if i + 1 < len(entries):
|
||||
comp.entries.pop(i)
|
||||
elif len(entries) > 1:
|
||||
comp.entries.pop(i)
|
||||
else:
|
||||
comp.entries = []
|
||||
comp.entries.pop(comp.idx())
|
||||
if 1 < len(comp.entries) - curses.LINES + 4 == comp.start:
|
||||
comp.start -= 1
|
||||
elif comp.idx() == len(comp.entries):
|
||||
comp.y -= 1
|
||||
comp.redraw()
|
||||
elif c == 105: # letter i
|
||||
extractor = comp.gets(_("Playlist extractor: "))
|
||||
filename = comp.gets(_("Insert: "))
|
||||
entries = extract_info(filename, extractor)
|
||||
if entries is None:
|
||||
comp.update_status(
|
||||
_("'{}': Can't extract playlist").format(filename))
|
||||
else:
|
||||
bottom = comp.entries[comp.idx():]
|
||||
comp.entries = comp.entries[:comp.idx()]
|
||||
comp.entries.extend(entries)
|
||||
comp.entries.extend(bottom)
|
||||
comp.redraw()
|
||||
elif c == 109: # letter m
|
||||
comp.mode = MODES[(MODES.index(comp.mode) + 1) % 8]
|
||||
comp.update_status()
|
||||
|
@ -422,9 +418,14 @@ with Comp(entries, json_file, mode, vid, vo, ytdlf) as comp:
|
|||
comp.next_search()
|
||||
elif c == 111: # letter o
|
||||
extractor = comp.gets(_("Playlist extractor: "))
|
||||
comp.entries = extract_info(comp.gets(_("Open: ")), extractor)
|
||||
comp.start, comp.y = 0, 1
|
||||
comp.redraw()
|
||||
filename = comp.gets(_("Open: "))
|
||||
entries = extract_info(filename, extractor)
|
||||
if entries is None:
|
||||
comp.update_status(
|
||||
_("'{}': Can't extract playlist").format(filename))
|
||||
else:
|
||||
comp.entries, comp.start, comp.y = entries, 0, 1
|
||||
comp.redraw()
|
||||
elif c == 112: # letter p
|
||||
comp.mp.pause ^= True
|
||||
elif c in (curses.KEY_UP, 107): # up arrow or letter k
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 811 KiB |
|
@ -0,0 +1,24 @@
|
|||
# omp - Omni Media Player
|
||||
# This is a part of comp
|
||||
#
|
||||
# comp is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# comp program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with comp. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
# Copyright (C) 2017 Nguyễn Gia Phong <vn.mcsinyx@gmail.com>
|
||||
|
||||
"""Omni Media Player - an handy mpv front-end library for interactive
|
||||
control.
|
||||
"""
|
||||
|
||||
from .ie import extract_info
|
||||
from .omp import Omp
|
|
@ -0,0 +1,114 @@
|
|||
# ie.py - Omni Media Player infomation extractor
|
||||
# This is a part of comp
|
||||
#
|
||||
# comp is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# comp program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with comp. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
# Copyright (C) 2017 Nguyễn Gia Phong <vn.mcsinyx@gmail.com>
|
||||
|
||||
import json
|
||||
from os.path import abspath, expanduser, expandvars, isfile
|
||||
from time import gmtime, sleep, strftime
|
||||
|
||||
from youtube_dl import YoutubeDL
|
||||
from mpv import MPV
|
||||
|
||||
DEFAULT_ENTRY = {'filename': '', 'title': '', 'duration': '00:00:00',
|
||||
'error': False, 'playing': False, 'selected': False}
|
||||
YTDL_OPTS = {'quiet': True, 'default_search': 'ytsearch',
|
||||
'extract_flat': 'in_playlist'}
|
||||
|
||||
|
||||
def json_extract_info(filename):
|
||||
"""Return list of entries extracted from a file using json. If an
|
||||
error occur during the extraction, return None.
|
||||
"""
|
||||
try:
|
||||
with open(filename) as f: raw_info, info = json.load(f), []
|
||||
for i in raw_info:
|
||||
e = DEFAULT_ENTRY.copy()
|
||||
for k in e:
|
||||
if k in i and isinstance(i[k], type(e[k])): e[k] = i[k]
|
||||
info.append(e)
|
||||
except:
|
||||
return None
|
||||
else:
|
||||
return info
|
||||
|
||||
|
||||
def mpv_extract_info(filename):
|
||||
"""Return list of entries extracted from a path or URL using mpv. If
|
||||
an error occur during the extraction, return None.
|
||||
"""
|
||||
mp = MPV(ytdl=True, vid=False)
|
||||
mp.play(filename)
|
||||
while mp.duration is None:
|
||||
sleep(0.25)
|
||||
if mp.playback_abort: return None
|
||||
info = {'filename': filename, 'title': mp.media_title.decode(),
|
||||
'duration': mp.osd.duration, 'error': False, 'playing': False,
|
||||
'selected': False}
|
||||
mp.quit()
|
||||
return [info]
|
||||
|
||||
|
||||
def ytdl_extract_info(filename):
|
||||
"""Return list of entries extracted from a path or URL using
|
||||
youtube-dl. If an error occur during the extraction, return None.
|
||||
"""
|
||||
with YoutubeDL(YTDL_OPTS) as ytdl:
|
||||
try:
|
||||
raw_info = ytdl.extract_info(filename, download=False)
|
||||
except:
|
||||
return None
|
||||
info = raw_info.get('entries', [raw_info])
|
||||
for i in info:
|
||||
if 'webpage_url' in i:
|
||||
i['filename'] = i['webpage_url']
|
||||
elif (i['ie_key'] == 'Youtube'
|
||||
or i['extractor'] == 'youtube'):
|
||||
i['filename'] = 'https://youtu.be/' + i['id']
|
||||
else:
|
||||
i['filename'] = i['url']
|
||||
if 'title' not in i:
|
||||
try:
|
||||
i['title'] = ytdl.extract_info(i['filename'],
|
||||
download=False)['title']
|
||||
except:
|
||||
return None
|
||||
if 'duration' not in i:
|
||||
i['duration'] = '00:00:00'
|
||||
elif isinstance(i['duration'], int):
|
||||
i['duration'] = strftime('%H:%M:%S', gmtime(i['duration']))
|
||||
for k in 'error', 'playing', 'selected': i.setdefault(k, False)
|
||||
for k in i.copy():
|
||||
if k not in DEFAULT_ENTRY: i.pop(k)
|
||||
return info
|
||||
|
||||
|
||||
def extract_info(filename, extractor='youtube-dl'):
|
||||
"""Return list of entries extracted from a path or URL using
|
||||
specified extractor. If an error occur during the extraction,
|
||||
return None.
|
||||
|
||||
The extractor could be either 'json', 'mpv' or 'youtube-dl' and
|
||||
fallback to 'youtube-dl'.
|
||||
"""
|
||||
if isfile(expanduser(expandvars(filename))):
|
||||
filename = abspath(expanduser(expandvars(filename)))
|
||||
if extractor == 'json':
|
||||
return json_extract_info(filename)
|
||||
elif extractor == 'mpv':
|
||||
return mpv_extract_info(filename)
|
||||
else:
|
||||
return ytdl_extract_info(filename)
|
Binary file not shown.
|
@ -0,0 +1,72 @@
|
|||
# SOME DESCRIPTIVE TITLE.
|
||||
# Copyright (C) YEAR ORGANIZATION
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"POT-Creation-Date: 2017-04-05 11:00+0700\n"
|
||||
"PO-Revision-Date: 2017-04-06 22:29+0700\n"
|
||||
"Language-Team: \n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Generated-By: pygettext.py 1.5\n"
|
||||
"X-Generator: Poedit 1.8.11\n"
|
||||
"Last-Translator: \n"
|
||||
"Plural-Forms: nplurals=1; plural=0;\n"
|
||||
"Language: vi_VN\n"
|
||||
|
||||
msgid "Curses Online Media Player"
|
||||
msgstr "Phần mềm chơi đa phương tiện trực tuyến sử dụng curses"
|
||||
|
||||
msgid "play-current"
|
||||
msgstr "chơi-một"
|
||||
|
||||
msgid "play-all"
|
||||
msgstr "chơi-tất-cả"
|
||||
|
||||
msgid "play-selected"
|
||||
msgstr "chơi-đã-chọn"
|
||||
|
||||
msgid "repeat-current"
|
||||
msgstr "lặp-một"
|
||||
|
||||
msgid "repeat-all"
|
||||
msgstr "lặp-tất-cả"
|
||||
|
||||
msgid "repeat-selected"
|
||||
msgstr "lặp-đã-chọn"
|
||||
|
||||
msgid "shuffle-all"
|
||||
msgstr "ngẫu-nhiên-tất-cả"
|
||||
|
||||
msgid "shuffle-selected"
|
||||
msgstr "ngẫu-nhiên-đã-chọn"
|
||||
|
||||
msgid "URL"
|
||||
msgstr "URL"
|
||||
|
||||
msgid "Title"
|
||||
msgstr "Tiêu đề"
|
||||
|
||||
msgid "Source"
|
||||
msgstr "Nguồn"
|
||||
|
||||
msgid "Current size: {}x{}. Minimum size: {}x4."
|
||||
msgstr "Kích thước hiện tại: {}x{}. Kích thước tối thiểu: {}x4."
|
||||
|
||||
msgid "Save playlist to [{}]:"
|
||||
msgstr "Lưu playlist tại [{}]:"
|
||||
|
||||
msgid "'{}': Can't open file for writing"
|
||||
msgstr "'{}': Không mở được tệp để ghi"
|
||||
|
||||
msgid "'{}' written"
|
||||
msgstr "'{}' đã ghi"
|
||||
|
||||
msgid "path to playlist in JSON format"
|
||||
msgstr "đường dẫn đến playlist ở định dạng JSON"
|
||||
|
||||
msgid "URL to an playlist on Youtube"
|
||||
msgstr "URL của playlist trên Youtube"
|
|
@ -1,4 +1,4 @@
|
|||
# omp.py - comp library for playing and playlist management
|
||||
# omp.py - Omni Media Player meta object
|
||||
# This is a part of comp
|
||||
#
|
||||
# comp is free software: you can redistribute it and/or modify
|
||||
|
@ -10,7 +10,6 @@
|
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with comp. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
@ -22,8 +21,8 @@ from collections import deque
|
|||
from itertools import cycle
|
||||
from os.path import abspath, expanduser, expandvars, isfile
|
||||
from random import choice
|
||||
from requests import head
|
||||
from time import gmtime, sleep, strftime
|
||||
from urllib import request
|
||||
|
||||
from youtube_dl import YoutubeDL
|
||||
from mpv import MPV, MpvFormat
|
||||
|
@ -34,79 +33,8 @@ YTDL_OPTS = {'quiet': True, 'default_search': 'ytsearch',
|
|||
'extract_flat': 'in_playlist'}
|
||||
|
||||
|
||||
def extract_info(filename, extractor='youtube-dl'):
|
||||
"""Return list of entries extracted from a path or URL using
|
||||
specified extractor.
|
||||
|
||||
The extractor could be either 'json', 'mpv' or 'youtube-dl'. If it
|
||||
is not one of them or not specified, youtube-dl will be used.
|
||||
"""
|
||||
def json_extract_info(filename):
|
||||
try:
|
||||
with open(filename) as f: raw_info = json.load(f)
|
||||
info = []
|
||||
for i in raw_info:
|
||||
e = DEFAULT_ENTRY.copy()
|
||||
for k in e:
|
||||
if k in i and isinstance(i[k], type(e[k])): e[k] = i[k]
|
||||
info.append(e)
|
||||
except:
|
||||
return []
|
||||
else:
|
||||
return info
|
||||
|
||||
def mpv_extract_info(filename):
|
||||
mp = MPV(ytdl=True)
|
||||
mp.play(filename)
|
||||
while mp.duration is None:
|
||||
sleep(0.25)
|
||||
if mp.playback_abort: return []
|
||||
info = {'filename': filename, 'title': mp.media_title.decode(),
|
||||
'duration': mp.osd.duration, 'error': False, 'playing': False,
|
||||
'selected': False}
|
||||
mp.quit()
|
||||
return [info]
|
||||
|
||||
def ytdl_extract_info(filename):
|
||||
with YoutubeDL(YTDL_OPTS) as ytdl:
|
||||
raw_info = ytdl.extract_info(filename, download=False)
|
||||
info = raw_info.get('entries', [raw_info])
|
||||
for i in info:
|
||||
if 'webpage_url' in i:
|
||||
i['filename'] = i['webpage_url']
|
||||
elif (i['ie_key'] == 'Youtube'
|
||||
or i['extractor'] == 'youtube'):
|
||||
i['filename'] = 'https://youtu.be/' + i['id']
|
||||
else:
|
||||
i['filename'] = i['url']
|
||||
if 'title' not in i:
|
||||
i['title'] = ytdl.extract_info(i['filename'],
|
||||
download=False)['title']
|
||||
if 'duration' not in i:
|
||||
i['duration'] = '00:00:00'
|
||||
elif isinstance(i['duration'], int):
|
||||
i['duration'] = strftime('%H:%M:%S', gmtime(i['duration']))
|
||||
for k in 'error', 'playing', 'selected': i.setdefault(k, False)
|
||||
for k in i.copy():
|
||||
if k not in DEFAULT_ENTRY: i.pop(k)
|
||||
return info
|
||||
|
||||
try:
|
||||
if (extractor != 'youtube-dl' and head(filename).status_code >= 400
|
||||
and isfile(expanduser(expandvars(filename)))):
|
||||
filename = abspath(expanduser(expandvars(filename)))
|
||||
except:
|
||||
pass
|
||||
if extractor == 'json':
|
||||
return json_extract_info(filename)
|
||||
elif extractor == 'mpv':
|
||||
return mpv_extract_info(filename)
|
||||
else:
|
||||
return ytdl_extract_info(filename)
|
||||
|
||||
|
||||
class Omp(object):
|
||||
"""Meta object for playing and playlist management.
|
||||
"""Omni Media Player meta object.
|
||||
|
||||
Attributes:
|
||||
entries (list): list of all tracks
|
||||
|
@ -174,7 +102,7 @@ class Omp(object):
|
|||
pass
|
||||
|
||||
def next(self, force=False, backward=False):
|
||||
comp.play_backward = backward
|
||||
self.play_backward = backward
|
||||
if self.mp.idle_active:
|
||||
self.play(force)
|
||||
else:
|
21
setup.py
21
setup.py
|
@ -1,8 +1,7 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
from os import walk
|
||||
from os import listdir
|
||||
from os.path import join
|
||||
from sys import prefix
|
||||
|
||||
from setuptools import setup
|
||||
|
||||
|
@ -11,8 +10,8 @@ with open('README.rst') as f:
|
|||
|
||||
setup(
|
||||
name='comp',
|
||||
version='0.3.1',
|
||||
description=('Curses Online Media Player'),
|
||||
version='0.3.2',
|
||||
description=('Curses Omni Media Player'),
|
||||
long_description=long_description,
|
||||
url='https://github.com/McSinyx/comp',
|
||||
author='Nguyễn Gia Phong',
|
||||
|
@ -29,16 +28,10 @@ setup(
|
|||
'Programming Language :: Python :: 3.5',
|
||||
'Programming Language :: Python :: 3.6',
|
||||
'Topic :: Multimedia :: Sound/Audio :: Players',
|
||||
'Topic :: Multimedia :: Video :: Display'
|
||||
],
|
||||
'Topic :: Multimedia :: Video :: Display'],
|
||||
keywords='youtube-dl mpv-wrapper curses console-application multimedia',
|
||||
packages=['omp'],
|
||||
install_requires=['python-mpv', 'youtube-dl'],
|
||||
data_files=[
|
||||
*((join(prefix, 'share', i[0]), [join(i[0], 'comp.mo')])
|
||||
for i in walk('locale') if i[2]),
|
||||
('/etc/comp', ['settings.ini'])
|
||||
],
|
||||
py_modules=['omp'],
|
||||
package_data={'omp': ['locale/*/LC_MESSAGES/omp.mo']},
|
||||
scripts=['comp'],
|
||||
platforms=['POSIX']
|
||||
)
|
||||
platforms=['POSIX'])
|
||||
|
|
Binary file not shown.
|
@ -0,0 +1,322 @@
|
|||
[
|
||||
{
|
||||
"duration": "00:05:21",
|
||||
"error": false,
|
||||
"filename": "https://youtu.be/weeI1G46q0o",
|
||||
"playing": false,
|
||||
"selected": true,
|
||||
"title": "DJ Khaled - I'm the One ft. Justin Bieber, Quavo, Chance the Rapper, Lil Wayne"
|
||||
},
|
||||
{
|
||||
"duration": "00:04:23",
|
||||
"error": false,
|
||||
"filename": "https://youtu.be/JGwWNGJdvx8",
|
||||
"playing": false,
|
||||
"selected": true,
|
||||
"title": "Ed Sheeran - Shape of You [Official Video]"
|
||||
},
|
||||
{
|
||||
"duration": "00:03:30",
|
||||
"error": false,
|
||||
"filename": "https://youtu.be/PMivT7MJ41M",
|
||||
"playing": false,
|
||||
"selected": true,
|
||||
"title": "Bruno Mars - That’s What I Like [Official Video]"
|
||||
},
|
||||
{
|
||||
"duration": "00:04:46",
|
||||
"error": false,
|
||||
"filename": "https://youtu.be/CTFtOOh47oo",
|
||||
"playing": false,
|
||||
"selected": false,
|
||||
"title": "French Montana - Unforgettable ft. Swae Lee"
|
||||
},
|
||||
{
|
||||
"duration": "00:04:45",
|
||||
"error": false,
|
||||
"filename": "https://youtu.be/NLZRYQMLDW4",
|
||||
"playing": false,
|
||||
"selected": false,
|
||||
"title": "Kendrick Lamar - DNA."
|
||||
},
|
||||
{
|
||||
"duration": "00:04:07",
|
||||
"error": false,
|
||||
"filename": "https://youtu.be/FM7MFYoylVs",
|
||||
"playing": false,
|
||||
"selected": true,
|
||||
"title": "The Chainsmokers & Coldplay - Something Just Like This (Lyric)"
|
||||
},
|
||||
{
|
||||
"duration": "00:03:48",
|
||||
"error": false,
|
||||
"filename": "https://youtu.be/72UO0v5ESUo",
|
||||
"playing": false,
|
||||
"selected": true,
|
||||
"title": "Luis Fonsi, Daddy Yankee - Despacito (Audio) ft. Justin Bieber"
|
||||
},
|
||||
{
|
||||
"duration": "00:00:00",
|
||||
"error": false,
|
||||
"filename": "https://youtu.be/D5drYkLiLI8",
|
||||
"playing": false,
|
||||
"selected": false,
|
||||
"title": "Kygo, Selena Gomez - It Ain't Me (with Selena Gomez) (Audio)"
|
||||
},
|
||||
{
|
||||
"duration": "00:00:00",
|
||||
"error": false,
|
||||
"filename": "https://youtu.be/Zgmvg-zzctI",
|
||||
"playing": false,
|
||||
"selected": false,
|
||||
"title": "Lil Uzi Vert - XO TOUR Llif3 (Produced By TM88)"
|
||||
},
|
||||
{
|
||||
"duration": "00:12:53",
|
||||
"error": false,
|
||||
"filename": "test/gplv3.ogg",
|
||||
"playing": false,
|
||||
"selected": true,
|
||||
"title": "gplv3.ogg"
|
||||
},
|
||||
{
|
||||
"duration": "00:00:00",
|
||||
"error": false,
|
||||
"filename": "https://youtu.be/xvZqHgFz51I",
|
||||
"playing": false,
|
||||
"selected": false,
|
||||
"title": "Future - Mask Off"
|
||||
},
|
||||
{
|
||||
"duration": "00:00:00",
|
||||
"error": false,
|
||||
"filename": "https://youtu.be/8j9zMok6two",
|
||||
"playing": false,
|
||||
"selected": false,
|
||||
"title": "Miley Cyrus - Malibu (Official Video)"
|
||||
},
|
||||
{
|
||||
"duration": "00:00:00",
|
||||
"error": false,
|
||||
"filename": "https://youtu.be/dPI-mRFEIH0",
|
||||
"playing": false,
|
||||
"selected": false,
|
||||
"title": "Katy Perry - Bon Appétit (Official) ft. Migos"
|
||||
},
|
||||
{
|
||||
"duration": "00:00:00",
|
||||
"error": false,
|
||||
"filename": "https://youtu.be/aatr_2MstrI",
|
||||
"playing": false,
|
||||
"selected": false,
|
||||
"title": "Clean Bandit - Symphony feat. Zara Larsson [Official Video]"
|
||||
},
|
||||
{
|
||||
"duration": "00:34:38",
|
||||
"error": false,
|
||||
"filename": "https://www.tube8.com/teen/nicole-ray-and-james-deen/409802/",
|
||||
"playing": false,
|
||||
"selected": true,
|
||||
"title": "Nicole Ray and James Deen"
|
||||
},
|
||||
{
|
||||
"duration": "00:00:00",
|
||||
"error": false,
|
||||
"filename": "https://youtu.be/7F37r50VUTQ",
|
||||
"playing": false,
|
||||
"selected": true,
|
||||
"title": "ZAYN, Taylor Swift - I Don’t Wanna Live Forever (Fifty Shades Darker)"
|
||||
},
|
||||
{
|
||||
"duration": "00:00:00",
|
||||
"error": false,
|
||||
"filename": "https://youtu.be/qFLhGq0060w",
|
||||
"playing": false,
|
||||
"selected": true,
|
||||
"title": "The Weeknd - I Feel It Coming ft. Daft Punk"
|
||||
},
|
||||
{
|
||||
"duration": "00:00:00",
|
||||
"error": false,
|
||||
"filename": "https://youtu.be/6ImFf__U6io",
|
||||
"playing": false,
|
||||
"selected": true,
|
||||
"title": "Birdman - Dark Shades (Explicit) ft. Lil Wayne, Mack Maine"
|
||||
},
|
||||
{
|
||||
"duration": "00:03:56",
|
||||
"error": false,
|
||||
"filename": "https://www.youtube.com/watch?v=3M3xfu0m5o4",
|
||||
"playing": false,
|
||||
"selected": false,
|
||||
"title": "David Banner - Play (Dirty version)"
|
||||
},
|
||||
{
|
||||
"duration": "00:00:00",
|
||||
"error": false,
|
||||
"filename": "https://youtu.be/NGLxoKOvzu4",
|
||||
"playing": false,
|
||||
"selected": false,
|
||||
"title": "Jason Derulo - Swalla (feat. Nicki Minaj & Ty Dolla $ign) (Official Music Video)"
|
||||
},
|
||||
{
|
||||
"duration": "00:00:00",
|
||||
"error": false,
|
||||
"filename": "https://youtu.be/Hm1YFszJWbQ",
|
||||
"playing": false,
|
||||
"selected": false,
|
||||
"title": "Migos - Slippery feat. Gucci Mane [Official Video]"
|
||||
},
|
||||
{
|
||||
"duration": "00:00:00",
|
||||
"error": false,
|
||||
"filename": "https://youtu.be/SC4xMk98Pdc",
|
||||
"playing": false,
|
||||
"selected": false,
|
||||
"title": "Post Malone - Congratulations ft. Quavo"
|
||||
},
|
||||
{
|
||||
"duration": "00:00:00",
|
||||
"error": false,
|
||||
"filename": "https://youtu.be/nfs8NYg7yQM",
|
||||
"playing": false,
|
||||
"selected": false,
|
||||
"title": "Charlie Puth - Attention [Official Video]"
|
||||
},
|
||||
{
|
||||
"duration": "00:04:10",
|
||||
"error": false,
|
||||
"filename": "https://www.youtube.com/watch?v=sRIkXM8S1J8",
|
||||
"playing": false,
|
||||
"selected": true,
|
||||
"title": "Best Goat Song Versions Compilation Ever! (HD)"
|
||||
},
|
||||
{
|
||||
"duration": "00:00:00",
|
||||
"error": false,
|
||||
"filename": "https://youtu.be/Dst9gZkq1a8",
|
||||
"playing": false,
|
||||
"selected": false,
|
||||
"title": "Travis Scott - goosebumps ft. Kendrick Lamar"
|
||||
},
|
||||
{
|
||||
"duration": "00:00:00",
|
||||
"error": false,
|
||||
"filename": "https://youtu.be/dMK_npDG12Q",
|
||||
"playing": false,
|
||||
"selected": false,
|
||||
"title": "Lorde - Green Light"
|
||||
},
|
||||
{
|
||||
"duration": "00:00:00",
|
||||
"error": false,
|
||||
"filename": "https://youtu.be/h--P8HzYZ74",
|
||||
"playing": false,
|
||||
"selected": true,
|
||||
"title": "Zedd, Alessia Cara - Stay (Lyric Video)"
|
||||
},
|
||||
{
|
||||
"duration": "00:00:00",
|
||||
"error": false,
|
||||
"filename": "https://youtu.be/Mdh2p03cRfw",
|
||||
"playing": false,
|
||||
"selected": false,
|
||||
"title": "Sam Hunt - Body Like A Back Road (Audio)"
|
||||
},
|
||||
{
|
||||
"duration": "00:00:00",
|
||||
"error": false,
|
||||
"filename": "https://youtu.be/Fq0xEpRDL9Q",
|
||||
"playing": false,
|
||||
"selected": false,
|
||||
"title": "Chris Brown - Privacy (Explicit Version)"
|
||||
},
|
||||
{
|
||||
"duration": "00:00:00",
|
||||
"error": false,
|
||||
"filename": "https://youtu.be/7wtfhZwyrcc",
|
||||
"playing": false,
|
||||
"selected": false,
|
||||
"title": "Imagine Dragons - Believer"
|
||||
},
|
||||
{
|
||||
"duration": "00:00:00",
|
||||
"error": false,
|
||||
"filename": "https://youtu.be/t_jHrUE5IOk",
|
||||
"playing": false,
|
||||
"selected": false,
|
||||
"title": "Maluma - Felices los 4 (Official Video)"
|
||||
},
|
||||
{
|
||||
"duration": "00:00:00",
|
||||
"error": false,
|
||||
"filename": "https://youtu.be/wzZWXrlDj-A",
|
||||
"playing": false,
|
||||
"selected": false,
|
||||
"title": "DNCE - Kissing Strangers ft. Nicki Minaj"
|
||||
},
|
||||
{
|
||||
"duration": "00:00:00",
|
||||
"error": false,
|
||||
"filename": "https://youtu.be/AEB6ibtdPZc",
|
||||
"playing": false,
|
||||
"selected": false,
|
||||
"title": "Paramore: Hard Times [OFFICIAL VIDEO]"
|
||||
},
|
||||
{
|
||||
"duration": "00:00:00",
|
||||
"error": false,
|
||||
"filename": "https://youtu.be/vqW18C4plZ8",
|
||||
"playing": false,
|
||||
"selected": false,
|
||||
"title": "WizKid - Come Closer ft. Drake"
|
||||
},
|
||||
{
|
||||
"duration": "00:00:00",
|
||||
"error": false,
|
||||
"filename": "https://youtu.be/A7xzXDStQnk",
|
||||
"playing": false,
|
||||
"selected": false,
|
||||
"title": "Shawn Mendes - There's Nothing Holdin' Me Back (Lyric Video)"
|
||||
},
|
||||
{
|
||||
"duration": "00:00:00",
|
||||
"error": false,
|
||||
"filename": "https://youtu.be/FG9M0aEpJGE",
|
||||
"playing": false,
|
||||
"selected": false,
|
||||
"title": "G-Eazy & Kehlani - Good Life (from The Fate of the Furious: The Album) [MUSIC VIDEO]"
|
||||
},
|
||||
{
|
||||
"duration": "00:00:00",
|
||||
"error": false,
|
||||
"filename": "https://youtu.be/vp8VZe5kqEM",
|
||||
"playing": false,
|
||||
"selected": false,
|
||||
"title": "Lady Gaga - The Cure (Audio)"
|
||||
},
|
||||
{
|
||||
"duration": "00:00:00",
|
||||
"error": false,
|
||||
"filename": "https://youtu.be/eP4eqhWc7sI",
|
||||
"playing": false,
|
||||
"selected": false,
|
||||
"title": "Lana Del Rey - Lust For Life (Official Video) ft. The Weeknd"
|
||||
},
|
||||
{
|
||||
"duration": "00:00:00",
|
||||
"error": false,
|
||||
"filename": "https://youtu.be/5qJp6xlKEug",
|
||||
"playing": false,
|
||||
"selected": false,
|
||||
"title": "Gorillaz - Saturnz Barz (Spirit House)"
|
||||
},
|
||||
{
|
||||
"duration": "00:00:00",
|
||||
"error": false,
|
||||
"filename": "https://youtu.be/9sg-A-eS6Ig",
|
||||
"playing": false,
|
||||
"selected": true,
|
||||
"title": "Enrique Iglesias - SUBEME LA RADIO (Official Video) ft. Descemer Bueno, Zion & Lennox"
|
||||
}
|
||||
]
|
Loading…
Reference in New Issue