Skip to content
GitLab
Explore
Sign in
Primary navigation
Search or go to…
Project
D
djrandom-py-deleted-112
Manage
Activity
Members
Labels
Plan
Issues
Issue boards
Milestones
Code
Merge requests
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Build
Pipelines
Jobs
Pipeline schedules
Artifacts
Deploy
Releases
Model registry
Operate
Environments
Monitor
Incidents
Service Desk
Analyze
Value stream analytics
Contributor analytics
CI/CD analytics
Repository analytics
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
GitLab community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
This project is pending deletion, and will be deleted on
2025-06-14
. Repository and other project resources are read-only.
Show more breadcrumbs
ale
djrandom-py-deleted-112
Commits
1b6fe7e4
Commit
1b6fe7e4
authored
13 years ago
by
ale
Browse files
Options
Downloads
Patches
Plain Diff
create a working FUSE client
parent
1d8b11d4
No related branches found
No related tags found
No related merge requests found
Changes
1
Show whitespace changes
Inline
Side-by-side
Showing
1 changed file
client/djrandom_client/djfuse.py
+103
-52
103 additions, 52 deletions
client/djrandom_client/djfuse.py
with
103 additions
and
52 deletions
client/djrandom_client/djfuse.py
+
103
−
52
View file @
1b6fe7e4
import
json
import
json
import
os
import
os
import
shelve
import
shutil
import
shutil
import
threading
import
urllib
import
urllib2
import
urllib2
from
errno
import
EINVAL
,
ENOENT
from
errno
import
EINVAL
,
ENOENT
from
stat
import
S_IFDIR
,
S_IFREG
from
stat
import
S_IFDIR
,
S_IFREG
from
fuse
import
FUSE
,
Operations
,
LoggingMixIn
from
fuse
import
FUSE
,
Operations
,
LoggingMixIn
class
DJAPI
(
object
):
def
__init__
(
self
,
server_url
,
api_key
=
'
fuse
'
):
self
.
_server_url
=
server_url
self
.
_api_key
=
api_key
self
.
_opener
=
urllib2
.
build_opener
()
def
get
(
self
,
url
):
print
'
HTTP GET:
'
,
url
req
=
urllib2
.
Request
(
self
.
_server_url
+
url
)
return
json
.
load
(
self
.
_opener
.
open
(
req
))
def
download
(
self
,
sha1
,
path
):
print
'
HTTP DOWNLOAD:
'
,
sha1
req
=
urllib2
.
Request
(
'
%s/dl/%s
'
%
(
self
.
_server_url
,
sha1
))
resp
=
self
.
_opener
.
open
(
req
)
with
open
(
path
,
'
wb
'
)
as
fd
:
shutil
.
copyfileobj
(
resp
,
fd
)
class
StatCache
(
object
):
def
__init__
(
self
,
api
):
self
.
_api
=
api
self
.
_cache
=
{}
self
.
_lock
=
threading
.
Lock
()
def
_key
(
self
,
artist
,
album
,
title
):
return
'
%s|%s|%s
'
%
(
artist
,
album
,
title
)
def
_load
(
self
,
artist
,
album
,
songs
):
with
self
.
_lock
:
dir_key
=
self
.
_key
(
artist
,
album
,
'
DIR
'
)
self
.
_cache
[
dir_key
]
=
songs
for
song
in
songs
:
key
=
self
.
_key
(
artist
,
album
,
song
[
'
title
'
])
self
.
_cache
[
key
]
=
song
def
preload
(
self
,
artist
,
album
):
contents
=
self
.
_api
.
get
(
'
/json/album/%s/%s
'
%
(
urllib
.
quote
(
artist
),
urllib
.
quote
(
album
)))[
'
songs
'
]
self
.
_load
(
artist
,
album
,
contents
)
def
get_dir
(
self
,
artist
,
album
):
return
self
.
get_file
(
artist
,
album
,
'
DIR
'
)
def
get_file
(
self
,
artist
,
album
,
title
):
print
'
CACHE: get_file
'
,
artist
,
album
,
title
key
=
self
.
_key
(
artist
,
album
,
title
)
with
self
.
_lock
:
result
=
self
.
_cache
.
get
(
key
)
if
not
result
:
self
.
preload
(
artist
,
album
)
with
self
.
_lock
:
result
=
self
.
_cache
.
get
(
key
)
return
result
class
DJFS
(
LoggingMixIn
,
Operations
):
class
DJFS
(
LoggingMixIn
,
Operations
):
"""
Simple FUSE client for djrandom.
"""
Simple FUSE client for djrandom.
...
@@ -20,65 +82,47 @@ class DJFS(LoggingMixIn, Operations):
...
@@ -20,65 +82,47 @@ class DJFS(LoggingMixIn, Operations):
if
not
os
.
path
.
isdir
(
cache_dir
):
if
not
os
.
path
.
isdir
(
cache_dir
):
os
.
makedirs
(
cache_dir
)
os
.
makedirs
(
cache_dir
)
self
.
_cache_dir
=
cache_dir
self
.
_cache_dir
=
cache_dir
self
.
_server_url
=
server_url
.
rstrip
(
'
/
'
)
self
.
_api
=
DJAPI
(
server_url
)
self
.
_opener
=
urllib2
.
build_opener
()
self
.
_stat_cache
=
StatCache
(
self
.
_api
)
self
.
_hash_cache
=
shelve
.
open
(
os
.
path
.
join
(
cache_dir
,
'
artist_album
'
))
def
_get
(
self
,
url
):
req
=
urllib2
.
Request
(
self
.
_server_url
+
url
)
return
json
.
load
(
self
.
_opener
.
open
(
req
))
@classmethod
def
_hash_cache_key
(
cls
,
artist
,
album
,
title
):
return
'
%s|%s|%s
'
%
(
artist
,
album
,
title
)
def
_load_into_cache
(
self
,
artist
,
album
,
songs
):
for
song
in
songs
:
key
=
self
.
_hash_cache_key
(
artist
,
album
,
song
[
'
title
'
])
self
.
_hash_cache
[
key
]
=
song
[
'
sha1
'
]
def
_trigger_cache_load
(
self
,
artist
,
album
):
def
destroy
(
self
,
path
):
contents
=
self
.
_get
(
'
/json/album/%s/%s
'
%
(
artist
,
album
))[
'
songs
'
]
pass
self
.
_load_into_cache
(
artist
,
album
,
contents
)
def
_find_sha1
(
self
,
artist
,
album
,
title
):
def
_find_sha1
(
self
,
artist
,
album
,
title
):
key
=
self
.
_hash_cache_key
(
artist
,
album
,
title
)
return
self
.
_stat_cache
.
get_file
(
artist
,
album
,
title
)
sha1
=
self
.
_hash_cache
.
get
(
key
)
if
not
sha1
:
def
_file_cache_path
(
self
,
sha1
):
self
.
_trigger_cache_load
(
artist
,
album
)
return
os
.
path
.
join
(
self
.
_cache_dir
,
sha1
[
0
],
sha1
)
sha1
=
self
.
_hash_cache
.
get
(
key
)
return
sha1
def
_in_file_cache
(
self
,
sha1
):
def
_in_file_cache
(
self
,
sha1
):
path
=
os
.
path
.
join
(
self
.
_cache_dir
,
sha1
[
0
],
sha1
)
return
os
.
path
.
exists
(
self
.
_file_cache_path
(
sha1
))
return
os
.
path
.
exists
(
path
)
def
_download_file
(
self
,
sha1
):
def
_download_file
(
self
,
sha1
):
path
=
os
.
path
.
join
(
self
.
_cache_dir
,
sha1
[
0
],
sha1
)
path
=
self
.
_file_cache_path
(
sha1
)
req
=
urllib2
.
Request
(
'
/dl/%s
'
%
sha1
)
dl_dir
=
os
.
path
.
dirname
(
path
)
resp
=
self
.
_opener
.
open
(
req
)
if
not
os
.
path
.
isdir
(
dl_dir
):
with
open
(
path
,
'
wb
'
)
as
fd
:
os
.
makedirs
(
dl_dir
)
shutil
.
copyfileobj
(
req
,
fd
)
self
.
_api
.
download
(
sha1
,
path
)
def
_parse_path
(
self
,
path
):
def
_parse_path
(
self
,
path
):
parts
=
path
.
split
(
'
/
'
)
parts
=
path
[
1
:]
.
split
(
'
/
'
)
if
len
(
parts
)
>
3
:
if
len
(
parts
)
>
3
:
raise
OSError
(
EINVAL
)
raise
OSError
(
EINVAL
)
if
len
(
parts
)
<
3
:
if
len
(
parts
)
<
3
:
parts
.
extend
([
None
for
x
in
(
3
-
len
(
parts
))])
parts
.
extend
([
None
for
x
in
range
(
3
-
len
(
parts
))])
if
parts
[
2
]
and
parts
[
2
].
endswith
(
'
.mp3
'
):
parts
[
2
]
=
parts
[
2
][:
-
4
]
return
parts
return
parts
def
getattr
(
self
,
path
,
fh
=
None
):
def
getattr
(
self
,
path
,
fh
=
None
):
artist
,
album
,
title
=
self
.
_parse_path
(
path
)
artist
,
album
,
title
=
self
.
_parse_path
(
path
)
if
title
:
if
title
:
s
ha1
=
self
.
_cache
_
get
(
artist
,
album
,
title
)
s
tats
=
self
.
_
stat_
cache
.
get
_file
(
artist
,
album
,
title
)
if
not
s
ha1
:
if
not
s
tats
:
raise
OSError
(
ENOENT
)
raise
OSError
(
ENOENT
)
stats
=
self
.
_get
(
'
/json/song/%s
'
%
sha1
)
mtime
=
float
(
stats
.
get
(
'
uploaded_at
'
,
'
0
'
))
mtime
=
float
(
stats
[
'
uploaded_at
'
])
return
dict
(
st_mode
=
(
S_IFREG
|
0444
),
st_nlink
=
1
,
return
dict
(
st_mode
=
(
S_IFREG
|
0444
),
st_nlink
=
1
,
st_size
=
int
(
stats
[
'
size
'
]
),
st_size
=
int
(
stats
.
get
(
'
size
'
,
0
)
),
st_ctime
=
mtime
,
st_ctime
=
mtime
,
st_mtime
=
mtime
,
st_mtime
=
mtime
,
st_atime
=
mtime
)
st_atime
=
mtime
)
...
@@ -91,20 +135,27 @@ class DJFS(LoggingMixIn, Operations):
...
@@ -91,20 +135,27 @@ class DJFS(LoggingMixIn, Operations):
if
title
:
if
title
:
raise
OSError
(
EINVAL
)
raise
OSError
(
EINVAL
)
if
not
artist
:
if
not
artist
:
values
=
self
.
_get
(
'
/json/artists
'
)[
'
artists
'
]
values
=
self
.
_
api
.
get
(
'
/json/artists
'
)[
'
artists
'
]
elif
not
album
:
elif
not
album
:
values
=
self
.
_get
(
'
/json/albums/%s
'
%
artist
)[
'
albums
'
]
values
=
self
.
_
api
.
get
(
'
/json/albums/%s
'
%
urllib
.
quote
(
artist
)
)
[
'
albums
'
]
else
:
else
:
# equal to trigger_cache_load(
)
songs
=
self
.
_stat_cache
.
get_dir
(
artist
,
album
)
contents
=
self
.
_get
(
'
/json/album/%s/%s
'
%
(
artist
,
album
))[
'
songs
'
]
if
not
songs
:
self
.
_load_into_cache
(
artist
,
album
,
contents
)
raise
OSError
(
ENOENT
)
values
=
[
x
[
'
sha1
'
]
for
x
in
c
on
tent
s
]
values
=
[
'
%s.mp3
'
%
(
x
[
'
title
'
]
||
'
UNKNOWN
'
)
for
x
in
s
on
g
s
]
return
[
'
.
'
,
'
..
'
]
+
[
x
for
x
in
values
]
return
[
'
.
'
,
'
..
'
]
+
[
x
.
encode
(
'
utf-8
'
)
for
x
in
values
]
def
read
(
self
,
path
,
size
,
offset
,
fh
=
None
):
def
read
(
self
,
path
,
size
,
offset
,
fh
=
None
):
if
not
self
.
_in_file_cache
(
path
):
artist
,
album
,
title
=
self
.
_parse_path
(
path
)
self
.
_download_file
(
path
)
if
not
title
:
with
open
(
path
,
'
rb
'
)
as
fd
:
raise
OSError
(
ENOENT
)
sha1
=
self
.
_find_sha1
(
artist
,
album
,
title
)
if
not
sha1
:
raise
OSError
(
ENOENT
)
if
not
self
.
_in_file_cache
(
sha1
):
self
.
_download_file
(
sha1
)
real_path
=
self
.
_file_cache_path
(
sha1
)
with
open
(
real_path
,
'
rb
'
)
as
fd
:
fd
.
seek
(
offset
)
fd
.
seek
(
offset
)
return
fd
.
read
(
size
)
return
fd
.
read
(
size
)
...
@@ -138,7 +189,7 @@ def main():
...
@@ -138,7 +189,7 @@ def main():
parser
.
error
(
'
Wrong number of args
'
)
parser
.
error
(
'
Wrong number of args
'
)
fuse
=
FUSE
(
DJFS
(
opts
.
server_url
,
opts
.
cache_dir
),
fuse
=
FUSE
(
DJFS
(
opts
.
server_url
,
opts
.
cache_dir
),
args
[
0
],
foreground
=
True
)
args
[
0
],
foreground
=
True
,
debug
=
True
)
if
__name__
==
'
__main__
'
:
if
__name__
==
'
__main__
'
:
...
...
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment