1# Copyright (c) 2014-2021 Cedric Bellegarde <cedric.bellegarde@adishatz.org>
2# This program is free software: you can redistribute it and/or modify
3# it under the terms of the GNU General Public License as published by
4# the Free Software Foundation, either version 3 of the License, or
5# (at your option) any later version.
6# This program is distributed in the hope that it will be useful,
7# but WITHOUT ANY WARRANTY; without even the implied warranty of
8# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9# GNU General Public License for more details.
10# You should have received a copy of the GNU General Public License
11# along with this program. If not, see <http://www.gnu.org/licenses/>.
12
13from gi.repository import Gio, GLib
14from gi.repository.Gio import FILE_ATTRIBUTE_TIME_ACCESS
15
16from time import time
17
18from lollypop.logger import Logger
19from lollypop.define import App, FileType
20
21
22def get_file_type(uri):
23    """
24        Get file type from file extension
25        @param uri as str
26    """
27    audio = ["3gp", "aa", "aac", "aax", "act", "aiff", "alac", "amr", "ape",
28             "au", "awb", "dct", "dss", "dvf", "flac", "gsm", "iklax", "ivs",
29             "m4a", "m4b", "m4p", "mmf", "mp3", "mpc", "msv", "nmf", "nsf",
30             "ogg", "opus", "ra", "raw", "rf64", "sln", "tta", "voc", "vox",
31             "wav", "wma", "wv", "webm", "8svx", "cda"]
32    other = ["7z", "arj", "deb", "pkg", "rar", "rpm", "tar.gz", "z", "zip",
33             "pdf", "doc", "docx", "xls", "xlsx", "ppt", "pptx", "db", "txt",
34             "mov", "avi", "html", "ini", "cue", "nfo", "lrc"]
35    image = ["ai", "bmp", "gif", "ico", "jpeg", "jpg",
36             "png", "ps", "psd", "svg", "tif"]
37    pls = ["pls", "m3u"]
38    split = uri.lower().split(".")
39    if len(split) < 2:
40        return FileType.UNKNOWN
41    elif split[-1] in audio:
42        return FileType.AUDIO
43    elif split[-1] in pls:
44        return FileType.PLS
45    elif split[-1] in image + other:
46        return FileType.OTHER
47    else:
48        return FileType.UNKNOWN
49
50
51def is_audio(info):
52    """
53        Return True if files is audio
54        @param info as Gio.FileInfo
55    """
56    audio = ["application/ogg", "application/x-ogg", "application/x-ogm-audio",
57             "audio/aac", "audio/mp4", "audio/mpeg", "audio/mpegurl",
58             "audio/ogg", "audio/vnd.rn-realaudio", "audio/vorbis",
59             "audio/x-flac", "audio/x-mp3", "audio/x-mpeg", "audio/x-mpegurl",
60             "audio/x-ms-wma", "audio/x-musepack", "audio/x-oggflac",
61             "audio/x-pn-realaudio", "application/x-flac", "audio/x-speex",
62             "audio/x-vorbis", "audio/x-vorbis+ogg", "audio/x-wav",
63             "x-content/audio-player", "audio/x-aac", "audio/m4a",
64             "audio/x-m4a", "audio/mp3", "audio/ac3", "audio/flac",
65             "audio/x-opus+ogg", "application/x-extension-mp4", "audio/x-ape",
66             "audio/x-pn-aiff", "audio/x-pn-au", "audio/x-pn-wav",
67             "audio/x-pn-windows-acm", "application/x-matroska",
68             "audio/x-matroska", "audio/x-wavpack", "video/mp4",
69             "audio/x-mod", "audio/x-mo3", "audio/x-xm", "audio/x-s3m",
70             "audio/x-it", "audio/aiff", "audio/x-aiff"]
71    if info is not None:
72        if info.get_content_type() in audio:
73            return True
74    return False
75
76
77def is_pls(info):
78    """
79        Return True if files is a playlist
80        @param info as Gio.FileInfo
81    """
82    if info is not None:
83        if info.get_content_type() in ["audio/x-mpegurl",
84                                       "application/xspf+xml",
85                                       "application/vnd.apple.mpegurl"]:
86            return True
87    return False
88
89
90def get_mtime(info):
91    """
92        Return Last modified time of a given file
93        @param info as Gio.FileInfo
94    """
95    mtime = info.get_attribute_as_string("time::modified")
96    if mtime is None:
97        return 0
98    else:
99        return int(mtime)
100
101
102def remove_oldest(path, timestamp):
103    """
104        Remove oldest files at path
105        @param path as str
106        @param timestamp as int
107    """
108    SCAN_QUERY_INFO = "%s" % FILE_ATTRIBUTE_TIME_ACCESS
109    try:
110        d = Gio.File.new_for_path(path)
111        infos = d.enumerate_children(SCAN_QUERY_INFO,
112                                     Gio.FileQueryInfoFlags.NONE,
113                                     None)
114        for info in infos:
115            f = infos.get_child(info)
116            if info.get_file_type() == Gio.FileType.REGULAR:
117                atime = int(info.get_attribute_as_string(
118                    FILE_ATTRIBUTE_TIME_ACCESS))
119                # File not used since one year
120                if time() - atime > timestamp:
121                    f.delete(None)
122    except Exception as e:
123        Logger.error("remove_oldest(): %s", e)
124
125
126def is_readonly(uri):
127    """
128        Check if uri is readonly
129    """
130    try:
131        if not uri:
132            return True
133        f = Gio.File.new_for_uri(uri)
134        info = f.query_info("access::can-write",
135                            Gio.FileQueryInfoFlags.NONE,
136                            None)
137        return not info.get_attribute_boolean("access::can-write")
138    except:
139        return True
140
141
142def create_dir(path):
143    """
144        Create dir
145        @param path as str
146    """
147    d = Gio.File.new_for_path(path)
148    if not d.query_exists():
149        try:
150            d.make_directory_with_parents()
151        except:
152            Logger.info("Can't create %s" % path)
153
154
155def install_youtube_dl():
156    try:
157        path = GLib.get_user_data_dir() + "/lollypop/python"
158        argv = ["pip3", "install", "-t", path, "-U", "youtube-dl"]
159        GLib.spawn_sync(None, argv, [], GLib.SpawnFlags.SEARCH_PATH, None)
160    except Exception as e:
161        Logger.error("install_youtube_dl: %s" % e)
162
163
164def get_youtube_dl():
165    """
166        Get youtube-dl path and env
167        @return (str, [])
168    """
169    if App().settings.get_value("recent-youtube-dl"):
170        python_path = GLib.get_user_data_dir() + "/lollypop/python"
171        path = "%s/bin/youtube-dl" % python_path
172        env = ["PYTHONPATH=%s" % python_path]
173        f = Gio.File.new_for_path(path)
174        if f.query_exists():
175            return (path, env)
176    if GLib.find_program_in_path("youtube-dl"):
177        return ("youtube-dl", [])
178    else:
179        return (None, [])
180
181
182# From eyeD3 start
183# eyeD3 is written and maintained by:
184# Travis Shirk <travis@pobox.com>
185def id3EncodingToString(encoding):
186    from lollypop.define import LATIN1_ENCODING, UTF_8_ENCODING
187    from lollypop.define import UTF_16_ENCODING, UTF_16BE_ENCODING
188    if encoding == LATIN1_ENCODING:
189        return "latin_1"
190    elif encoding == UTF_8_ENCODING:
191        return "utf_8"
192    elif encoding == UTF_16_ENCODING:
193        return "utf_16"
194    elif encoding == UTF_16BE_ENCODING:
195        return "utf_16_be"
196    else:
197        raise ValueError("Encoding unknown: %s" % encoding)
198
199
200# From eyeD3 start
201def decodeUnicode(bites, encoding):
202    codec = id3EncodingToString(encoding)
203    return bites.decode(codec)
204
205
206def splitUnicode(data, encoding):
207    from lollypop.define import UTF_16_ENCODING, UTF_16BE_ENCODING
208    try:
209        (d, t) = data.split(encoding, 1)
210        # Try to fix invalid UTF16
211        if encoding == UTF_16_ENCODING or encoding == UTF_16BE_ENCODING:
212            if t[1] == 0:
213                t = b''.join(t.split(b'\x00')) + b'\x00'
214    except ValueError as e:
215        Logger.warning("splitUnicode(): %s", e)
216        (d, t) = data, b""
217    return (d, t)
218# From eyeD3 end
219