Compare commits

..

9 commits

Author SHA1 Message Date
Ben Goldsworthy 38e4057ca3
progress towards SQL data storage 2024-05-05 10:51:22 +01:00
Ben Goldsworthy e4a2572b89
update packages 2024-05-05 10:50:44 +01:00
Ben Goldsworthy ab9764b520
absolute poster paths 2024-05-05 10:50:33 +01:00
Ben Goldsworthy e3f41446d1
delete interim log 2024-05-05 10:49:50 +01:00
Ben Goldsworthy 932481d6b1
update logs 2024-05-05 10:49:32 +01:00
Ben Goldsworthy 815db4e9ac
add quote 2024-05-05 10:43:18 +01:00
Ben Goldsworthy 4bad49a8db
wip2 2024-04-15 20:41:04 +01:00
Ben Goldsworthy 9c1adf0e45
wip: process log 2024-04-13 11:20:34 +01:00
Ben Goldsworthy 3e1448dade
update logs 2024-04-13 11:20:19 +01:00
22 changed files with 7008 additions and 14446 deletions

View file

@ -0,0 +1,38 @@
---
title: Enchiridion
---
> Has any man been preferred before you at a benquet, or in being saluted, or in benig invited to a consultation? If these things are good, you ought to rejoice that he has obtained them: but if bad, be not grieved because you have not obtained them; and remember that you cannot, if you do not the same things in order to obtain what is not in our power, be considered worthy of the same (equal) things. For how can a man obtain an equal share with another when he does not visit a man's doors as that other man does, when he does not attend him when he goes abroad, as the other man does; when he does not praise (flatter) him as another does? You will be unjust then and insatiable, if you do not part with the price, in return for which those things are sold, and if you wish to obtain them for nothing.
> pp 9--11, XXV
> If a man has reported to you, that a certain person speaks ill of you, do not make any defense (answer) to what has been told you: by reply, The man did not know the rest of my faults, for he would not have mentioned these only.
> p 15, XXXIII
> When you are going to any of those who are in great power, place before yourself that you will not find the man at home, that you will be excluded, that the door will not be opened to you, that the man will not care about you. ANd if with all this it is your duty to visit him, bear what happens, and never say to yourself that it was not worth the trouble. For this is silly, and marks the character of a man who is offended by externals.
> p 16, XXXIII
> When you have decided that a thing out to be done and are doing it, never avoid being seen doing it, though the many shall form an unfavorable opinion about it. For is it is not right to do it, avoid doing then thing; but if it is right, why are you afraid of those who shall find fault wrongly?
> pp 16--17, XXXV
> When any person treats you ill or speaks ill of you, remember that he does this or says this because he thinks that it is his duty. It is not possible then for him to follow that which seems right to you, but that which seems right to himself. Accordingly if he is wrong in his opinion, he is the person who is hurt, for he is the person who has been deceived; for is a man shall suppose the true conjuction to be false, it is not the conjuction which is hindered, but the man who has been deceived about it. If you proceed then from these opinions, you will be mild in temper to him who reviles:for say on each occasion, It seemd so to him.
> p 18, XLII
> On no occasion call yourself a philosopher, and do not speak much among the uninstructed about theorems (philsophical rules, precepts): but do that which follows from them… Accordingly if any conversation should arise among uninstructed perons about any theorem, generally be silent; **for there is great danger that you will immediately vomit up what you have not digested**… For even sheep do not vomit up their grass and show to the shepherds how much they have eaten; but when they have internally digested the pasture, they produce externally woll and milk. Do you also show not your theorems to the uninstructed, but show the acts which come from their digestion.
> p 19, XLVI
### Fragments of Epictetus
> You will do the greatest services to the state, if you shall raise not the roofs of the houses, but the souls of the citizens: for it is better that great souls should dwell in small houses than for mean slaves to lurk in great houses.
> p 39, LXXXI
> When Thales was asked what is most universal, he answered, Hope, for hope stays with those who have nothing else.
> p 41, XCI
> It is more necessary to heal the soul than the body, for to die is better than to live a bad life.
> p 41, XCII
> To admonish is better than to reproach: for admonition is mild and friendly, but reproach is harsh and insulting; and admonition corrects those who are doing wrong, but reproach only convicts them.
> p 44, CVII
> **If you wish to live a life free from sorrow, think of what is going to happen as if it had already happened.**
> p 53, CLVIII

View file

@ -10,47 +10,50 @@ categories:
---
<blockquote class="no-first-blockquote">
<p>
<q>She&#8217;s a commie,</q> he said, as matter-of-factly as if he were saying, <q>She&#8217;s tall.</q> He had considered and rejected this possibility as well. <q>She started being one during the depression. She likes to talk about it, but I never understood any of that shit about workers wanting commies to run the unions and factories. I never met any who wanted that. But she thought that&#8217;s what they wanted and the union paid her to organize workers to want that. After the war the union threw her out on her ass and not one worker stood up for her. She still thinks that&#8217;s what everyone wants.</q>
<q>She's a commie,</q> he said, as matter-of-factly as if he were saying, <q>She's tall.</q> He had considered and rejected this possibility as well. <q>She started being one during the depression. She likes to talk about it, but I never understood any of that shit about workers wanting commies to run the unions and factories. I never met any who wanted that. But she thought that's what they wanted and the union paid her to organize workers to want that. After the war the union threw her out on her ass and not one worker stood up for her. She still thinks that's what everyone wants.</q>
</p><footer>Sophia Nachalo and Ron</footer>
</blockquote>
> <q>But my good fellow.</q> Zdenek shouted, <q>don&#8217;t you see that it&#8217;s impossible to overthrow a ruling social order with organisation and discipline? What you&#8217;re talking about is the reinstatement of the ruling order, not its overthrow.</q><footer>Yarostan Vochek and Zdenek</footer>
> <q>But my good fellow.</q> Zdenek shouted, <q>don't you see that it's impossible to overthrow a ruling social order with organisation and discipline? What you're talking about is the reinstatement of the ruling order, not its overthrow.</q><footer>Yarostan Vochek and Zdenek</footer>
> I can&#8217;t formulate either my goals or my means. I can tell you neither where I&#8217;m going nor how I&#8217;ll get there. Yet I feel more vibrant, more alive, than I felt when I thought I knew my direction and my destination because I had words for them. I feel alive precisely because I don&#8217;t know what the next moment will bring.<footer>Yarostan</footer>
> What I mean by “daring” is a readiness to walk into terrain which none of us explored before. What I mean by “caution” is the perception that our ability to approach this terrain grows only to the extent that all those like us approach it with equal daring. Were reaching for a field of possibilities that can be reached only if we move together as weve never moved before; we proceed with caution because those who move too far ahead will be caught without a lifeline to the rest. What I think is taking place around me is an advance consisting of small steps taken by all simultaneously. Each small step creates the conditions for taking the next. Any move that prevents the continued advance of all cuts off the possibility of further advance by any. All around me human beings are attempting to come to life as human beings, as universal individuals, as species beings, each advancing with all and all with each.
> Yarostan
> Despite all that&#8217;s happened during the past fourteen years, Daman has somehow managed not to change a single one of his ideas!&#8230;He could have put all his views on a phonograph record fourteen years ago and anyone who wanted to meet him could simply play the record. That&#8217;s eerie. Daman isn&#8217;t altogether a living person.<footer>Sophia</footer>
> I can't formulate either my goals or my means. I can tell you neither where I'm going nor how I'll get there. Yet I feel more vibrant, more alive, than I felt when I thought I knew my direction and my destination because I had words for them. I feel alive precisely because I don't know what the next moment will bring.<footer>Yarostan</footer>
> A prisoner whose helplessness leads him to seek out guards who are <q>on our side</q> is terribly similar to the worker who thinks a politician is <q>on our side.</q> The prisoner&#8217;s justification is that the guards are armed. The prisoner&#8217;s human prospects do in fact reside in the guard. But a worker who thinks his human prospects reside in the politician is deluded.<footer>Yarostan</footer>
> Despite all that's happened during the past fourteen years, Daman has somehow managed not to change a single one of his ideas!…He could have put all his views on a phonograph record fourteen years ago and anyone who wanted to meet him could simply play the record. That's eerie. Daman isn't altogether a living person.<footer>Sophia</footer>
> I know that the only way you&#8217;d ever go to a university building would be with a stick of dynamite in your hand.<footer>Tina</footer>
> A prisoner whose helplessness leads him to seek out guards who are <q>on our side</q> is terribly similar to the worker who thinks a politician is <q>on our side.</q> The prisoner's justification is that the guards are armed. The prisoner's human prospects do in fact reside in the guard. But a worker who thinks his human prospects reside in the politician is deluded.<footer>Yarostan</footer>
> You have your reasons. But your reasons aren&#8217;t good enough for me. They don&#8217;t grow out of my own life. I do things for Sabina&#8217;s reasons and I do others for Sophia&#8217;s but I never do anything for my own reasons. I don&#8217;t even know what my own reasons are. And that&#8217;s all I want right now. To discover my own reasons. To become me, Tina, a human entity, someone who&#8217;s neither Sophia nor Sabina.<footer>Tina</footer>
> I know that the only way you'd ever go to a university building would be with a stick of dynamite in your hand.<footer>Tina</footer>
> Finally I admitted, <q>I&#8217;m completely lost. I don&#8217;t understand you, Sabina&#8230;And I don&#8217;t see how I fit into it all!</q>
> Sabina reached for my hand and said, looking straight into my eyes, <q>There&#8217;s nothing to understand, Sophia, and nothing to fit into. It&#8217;s your life to do with as you will. There&#8217;s no structure. Nothing is banned. Everything is allowed. No holds are barred.</q>
> <q>What&#8217;s everything?</q> I asked hesitantly.
> You have your reasons. But your reasons aren't good enough for me. They don't grow out of my own life. I do things for Sabina's reasons and I do others for Sophia's but I never do anything for my own reasons. I don't even know what my own reasons are. And that's all I want right now. To discover my own reasons. To become me, Tina, a human entity, someone who's neither Sophia nor Sabina.<footer>Tina</footer>
> Finally I admitted, <q>I'm completely lost. I don't understand you, Sabina…And I don't see how I fit into it all!</q>
> Sabina reached for my hand and said, looking straight into my eyes, <q>There's nothing to understand, Sophia, and nothing to fit into. It's your life to do with as you will. There's no structure. Nothing is banned. Everything is allowed. No holds are barred.</q>
> <q>What's everything?</q> I asked hesitantly.
> Letting go of my hand, she said, <q>My life, my desires, my capacities; those are my axioms.</q><footer>Sophia and Sabina</footer>
> A person freely creates her own life, but in circumstances not of her own choosing.<footer>Sabina</footer>
> Every person who comes into this room has an altogether different account of what&#8217;s happening; each person has different stories to tell. And it&#8217;s precisely this that makes every encounter so stimulating.<footer>Sophia</footer>
> Every person who comes into this room has an altogether different account of what's happening; each person has different stories to tell. And it's precisely this that makes every encounter so stimulating.<footer>Sophia</footer>
> <q>I wouldn&#8217;t feel bad for having a bad memory, Sophia, but for having to take someone else&#8217;s word about an event I had experienced. How can you let everything in your head just lie where it falls, without ever moving it around? There&#8217;s no such thing as a bad memory; you&#8217;re just lazy!</q><footer>Sabina</footer>
> <q>I wouldn't feel bad for having a bad memory, Sophia, but for having to take someone else's word about an event I had experienced. How can you let everything in your head just lie where it falls, without ever moving it around? There's no such thing as a bad memory; you're just lazy!</q><footer>Sabina</footer>
> It&#8217;s easy to have reservations, Sophia. I didn&#8217;t act on them, and that&#8217;s all that counts.<footer>Sabina</footer>
> It's easy to have reservations, Sophia. I didn't act on them, and that's all that counts.<footer>Sabina</footer>
> Alberts affirmed technology; he rebelled against everything that constrained the further development of productive forces. That&#8217;s why he ended up considering human beings reactionary. Human beings constrain the development of productive forces; human beings have to be overcome. The beings who would inhabit the crystal palace wouldn&#8217;t be human beings.<footer>Sabina</footer>
> Alberts affirmed technology; he rebelled against everything that constrained the further development of productive forces. That's why he ended up considering human beings reactionary. Human beings constrain the development of productive forces; human beings have to be overcome. The beings who would inhabit the crystal palace wouldn't be human beings.<footer>Sabina</footer>
> Yara showed me that what my mother had called the devil is what&#8217;s most natural in all of us, what we feel; it&#8217;s our desires and our passions; it&#8217;s what we are. No sword is needed to embed the devil in us; the devil is already there; it&#8217;s the removal of the devil that requires a sword.<footer>Mirna</footer>
> Yara showed me that what my mother had called the devil is what's most natural in all of us, what we feel; it's our desires and our passions; it's what we are. No sword is needed to embed the devil in us; the devil is already there; it's the removal of the devil that requires a sword.<footer>Mirna</footer>
> Was I really what Yara had called me: a hypocrite who applauded at a great distance acts which I dared not undertake in my own home and neighborhood?<footer>Yarostan</footer>
> According to official accounts, an army of four million men is massed at our frontiers. Four million! In some circles they&#8217;re described as <q>barbarian hordes,</q> but I&#8217;m sure the vast majority of them are workers, exactly like the people they&#8217;re coming to repress&#8230;It would be more comforting to think the invaders were creatures from another planet, or insects. What is so terrifying is the thought that the invaders are workers like ourselves, workers who may next week be repressed by armies consisting of some of the very workers they are repressing now. It isn&#8217;t <q>they,</q> <q>the enemy,</q> who are driving those tanks and carrying those rifles. It&#8217;s <q>we</q> &mdash; we comrades, fellow workers, brothers, we who failed to communicate with each other, we who failed to destroy the schools where we&#8217;re taught to produce the tanks &mdash; the schools where we&#8217;re taught to obey the commanders who order us to assassinate each other.<footer>Yarostan</footer>
> According to official accounts, an army of four million men is massed at our frontiers. Four million! In some circles they're described as <q>barbarian hordes,</q> but I'm sure the vast majority of them are workers, exactly like the people they're coming to repress…It would be more comforting to think the invaders were creatures from another planet, or insects. What is so terrifying is the thought that the invaders are workers like ourselves, workers who may next week be repressed by armies consisting of some of the very workers they are repressing now. It isn't <q>they,</q> <q>the enemy,</q> who are driving those tanks and carrying those rifles. It's <q>we</q> we comrades, fellow workers, brothers, we who failed to communicate with each other, we who failed to destroy the schools where we're taught to produce the tanks — the schools where we're taught to obey the commanders who order us to assassinate each other.<footer>Yarostan</footer>
> What kind of system can afford to support a permanent force of four million trained assassins? Can you even imagine how much of society&#8217;s activity has to be concentrated on war-related work to supply an army of four million &mdash; in <q>peace time</q>?<footer>Yarostan</footer>
> What kind of system can afford to support a permanent force of four million trained assassins? Can you even imagine how much of society's activity has to be concentrated on war-related work to supply an army of four million — in <q>peace time</q>?<footer>Yarostan</footer>
> I don&#8217;t have the instincts of ants or bees; I can&#8217;t function in a hive.<footer>Yarostan</footer>
> I don't have the instincts of ants or bees; I can't function in a hive.<footer>Yarostan</footer>
> My very dreams were contaminated by the monstrosity he [Titus Zabran] stood for: the will to impose mental constructs on living people &mdash; which as Zdenek so perceptively pointed out can only be done by means of <q>historically available</q> instruments: guns, tanks, police and armies.<footer>Sophia</footer>
> My very dreams were contaminated by the monstrosity he [Titus Zabran] stood for: the will to impose mental constructs on living people which as Zdenek so perceptively pointed out can only be done by means of <q>historically available</q> instruments: guns, tanks, police and armies.<footer>Sophia</footer>
> I think the split was between the world of those who, like Ted, Jan and Mirna, sought to realize their own potentialities among others realizing theirs, and the world of those who, like Luisa and Daman and Titus Zabran, sought to fit human beings into what Sabina called a crystal palace, which in practice was always the same regimented barracks, the hive you&#8217;ve rejected. Like Sabina, and like me, you had a foot in both worlds.<footer>Sophia</footer>
> I think the split was between the world of those who, like Ted, Jan and Mirna, sought to realize their own potentialities among others realizing theirs, and the world of those who, like Luisa and Daman and Titus Zabran, sought to fit human beings into what Sabina called a crystal palace, which in practice was always the same regimented barracks, the hive you've rejected. Like Sabina, and like me, you had a foot in both worlds.<footer>Sophia</footer>

View file

@ -0,0 +1,219 @@
[
{
"name": "Let's Check in on AIPAC's Assault on the Squad",
"air_date": "2024-05-04",
"episode_number": 287,
"season_number": 1,
"series": "Deconstructed",
"date_added": "2024-05-04",
"date_finished": "2024-05-04"
},
{
"name": "Judith Butler Will Not Co-Sign Israel's Alibi for Genocide",
"air_date": "2024-05-01",
"episode_number": 300,
"season_number": 1,
"series": "Intercepted",
"date_added": "2024-05-02",
"date_finished": "2024-05-02"
},
{
"name": "Councils of Despair",
"air_date": "2024-04-30",
"episode_number": 104,
"season_number": 1,
"series": "Page 94: The Private Eye Podcast",
"date_added": "2024-05-02",
"date_finished": "2024-04-30"
},
{
"name": "Wearable Joe Biden Simulator LED Consent Light",
"air_date": "2024-04-30",
"episode_number": 774,
"season_number": 1,
"series": "Trashfuture",
"date_added": "2024-05-02",
"date_finished": "2024-04-30"
},
{
"name": "Biden's Indifference to Palestinian Lives Is Sending the Middle East Into the Abyss",
"air_date": "2024-04-24",
"episode_number": 299,
"season_number": 1,
"series": "Intercepted",
"date_added": "2024-04-24",
"date_finished": "2024-04-24"
},
{
"name": "Intercontinental Amuse-Bouche Barrage feat. Séamus Malekafzali",
"air_date": "2024-04-22",
"episode_number": 773,
"season_number": 1,
"series": "Trashfuture",
"date_added": "2024-04-23",
"date_finished": "2024-04-23"
},
{
"name": "U.S. Doctor Returning From Gaza Describes Unforgettable Carnage",
"air_date": "2024-04-17",
"episode_number": 298,
"season_number": 1,
"series": "Intercepted",
"date_added": "2024-04-17",
"date_finished": "2024-04-17"
},
{
"name": "Lessons",
"air_date": "2024-03-12",
"episode_number": 9,
"season_number": 1,
"series": "Organize the Unorganized: The Rise of the CIO",
"date_added": "2024-04-16",
"date_finished": "2024-04-16"
},
{
"name": "Is There an Ending to the CIO",
"air_date": "2024-03-05",
"episode_number": 8,
"season_number": 1,
"series": "Organize the Unorganized: The Rise of the CIO",
"date_added": "2024-04-16",
"date_finished": "2024-04-16"
},
{
"name": "War",
"air_date": "2024-02-26",
"episode_number": 7,
"season_number": 1,
"series": "Organize the Unorganized: The Rise of the CIO",
"date_added": "2024-04-16",
"date_finished": "2024-04-16"
},
{
"name": "From the Docks to the Killing Floors",
"air_date": "2024-02-19",
"episode_number": 6,
"season_number": 1,
"series": "Organize the Unorganized: The Rise of the CIO",
"date_added": "2024-04-16",
"date_finished": "2024-04-16"
},
{
"name": "Little Steel",
"air_date": "2024-02-05",
"episode_number": 5,
"season_number": 1,
"series": "Organize the Unorganized: The Rise of the CIO",
"date_added": "2024-04-16",
"date_finished": "2024-04-16"
},
{
"name": "Taking Stock",
"air_date": "2024-01-30",
"episode_number": 4,
"season_number": 1,
"series": "Organize the Unorganized: The Rise of the CIO",
"date_added": "2024-04-16",
"date_finished": "2024-04-16"
},
{
"name": "Site Down!",
"air_date": "2024-01-23",
"episode_number": 3,
"season_number": 1,
"series": "Organize the Unorganized: The Rise of the CIO",
"date_added": "2024-04-16",
"date_finished": "2024-04-16"
},
{
"name": "Powerful Personalities",
"air_date": "2024-01-16",
"episode_number": 2,
"season_number": 1,
"series": "Organize the Unorganized: The Rise of the CIO",
"date_added": "2024-04-16",
"date_finished": "2024-04-16"
},
{
"name": "Under the Blue Eagle",
"air_date": "2024-01-08",
"episode_number": 1,
"season_number": 1,
"series": "Organize the Unorganized: The Rise of the CIO",
"date_added": "2024-04-16",
"date_finished": "2024-04-16"
},
{
"name": "The Missing Women of Afrin",
"air_date": "2020-08-10",
"episode_number": 87,
"season_number": 1,
"series": "Popular Front Podcast",
"date_added": "2024-04-16",
"date_finished": "2024-04-16"
},
{
"name": "The End of the Women's War",
"air_date": "2020-05-13",
"episode_number": 7,
"season_number": 1,
"series": "The Women's War",
"date_added": "2024-04-16",
"date_finished": "2024-04-16"
},
{
"name": "The Women Warriors",
"air_date": "2020-05-06",
"episode_number": 6,
"season_number": 1,
"series": "The Women's War",
"date_added": "2024-04-16",
"date_finished": "2024-04-16"
},
{
"name": "On Patrol in ISIS's Old Capital",
"air_date": "2020-04-29",
"episode_number": 5,
"season_number": 1,
"series": "The Women's War",
"date_added": "2024-04-16",
"date_finished": "2024-04-16"
},
{
"name": "Grandma Law and Revolutionary Sacrifice",
"air_date": "2020-04-22",
"episode_number": 4,
"season_number": 1,
"series": "The Women's War",
"date_added": "2024-04-15",
"date_finished": "2024-04-15"
},
{
"name": "A Town for Women",
"air_date": "2020-04-15",
"episode_number": 3,
"season_number": 1,
"series": "The Women's War",
"date_added": "2024-04-15",
"date_finished": "2024-04-15"
},
{
"name": "Law and Order Among the Anarchists",
"air_date": "2020-04-08",
"episode_number": 2,
"season_number": 1,
"series": "The Women's War",
"date_added": "2024-04-15",
"date_finished": "2024-04-15",
"is_rewatch": true
},
{
"name": "Sneaking Into Utopia",
"air_date": "2020-04-01",
"episode_number": 1,
"season_number": 1,
"series": "The Women's War",
"date_added": "2024-04-15",
"date_finished": "2024-04-15"
}
]

View file

@ -0,0 +1,34 @@
[
{
"name": "The Bugle",
"date_added": "2024-04-23"
},
{
"name": "Behind the Bastards",
"date_added": "2024-04-16"
},
{
"name": "Blowback",
"date_added": "2024-04-17"
},
{
"name": "Page 94: The Private Eye Podcast",
"date_added": "2024-04-16"
},
{
"name": "Deconstructed",
"date_added": "2024-04-16"
},
{
"name": "Intercepted",
"date_added": "2024-04-16"
},
{
"name": "Trashfuture",
"date_added": "2024-04-23"
},
{
"name": "Chapo Trap House",
"date_added": "2024-04-23"
}
]

View file

@ -0,0 +1,88 @@
[
{
"name": "Organize the Unorganized: The Rise of the CIO",
"date_added": "2024-04-23",
"date_started": "2024-04-16",
"date_finished": "2024-04-16"
},
{
"first_air_date": "2020-04-01",
"name": "The Women's War",
"date_added": "2024-04-15",
"date_started": "2024-04-15",
"date_finished": "2024-04-16"
},
{
"name": "DVDASA",
"date_added": "2024-04-23"
},
{
"name": "The Lazarus Heist",
"date_added": "2024-04-24"
},
{
"name": "The Joe Rogan Experience",
"date_added": "2024-04-23"
},
{
"name": "Cum Town",
"date_added": "2024-04-23"
},
{
"name": "This American Life",
"date_added": "2024-04-16",
"date_started": "2023-03-24"
},
{
"name": "American ISIS",
"date_added": "2024-04-16"
},
{
"name": "Fall of Civilizations",
"date_added": "2024-04-16"
},
{
"name": "Hardcore History",
"date_added": "2024-04-16"
},
{
"name": "Laser Time",
"date_added": "2024-04-16"
},
{
"name": "Vidjagame Apocalypse",
"date_added": "2024-04-16"
},
{
"name": "TalkRadar",
"date_added": "2024-04-16"
},
{
"name": "TalkRadar UK",
"date_added": "2024-04-16"
},
{
"name": "Today",
"date_added": "2024-04-16"
},
{
"name": "Darknet Diaries",
"date_added": "2024-04-16"
},
{
"name": "404 Media",
"date_added": "2024-04-16"
},
{
"name": "The Ex-Worker",
"date_added": "2024-04-16"
},
{
"name": "The Hotwire",
"date_added": "2024-04-16"
},
{
"name": "This is America",
"date_added": "2024-04-16"
}
]

File diff suppressed because it is too large Load diff

View file

@ -1,4 +1,65 @@
[
{
"publishers": [
"Dover Publications"
],
"isbn_10": "0486433595",
"series": [
"Dover thrift editions"
],
"covers": [
314053
],
"authors": [
{
"ol_id": "OL22801A",
"name": "Epictetus",
"personal_name": "Epictetus."
}
],
"genres": [
"Early works to 1800."
],
"title": "Enchiridion",
"number_of_pages": 56,
"languages": [
"eng"
],
"subjects": [
"Ethics, Ancient",
"Conduct of life -- Early works to 1800"
],
"publish_date": "2004",
"publish_country": "nyu",
"by_statement": "Epictetus ; translated by George Long.",
"work_title": [
"Manual."
],
"work": {
"ol_id": "OL16233356W",
"title": "Manual",
"authors": [
{
"ol_id": "OL22801A",
"name": "Epictetus",
"personal_name": "Epictetus."
}
],
"subjects": [
"Early works to 1800",
"Conduct of life",
"Ancient Ethics",
"Ethics",
"Ancient Philosophy"
],
"date_added": "2024-03-27"
},
"ol_id": "OL3693390M",
"date_added": "2024-03-27",
"date_started": "2024-03-27",
"date_finished": "2024-03-27",
"added_by_id": "9780486433592"
},
{
"publishers": [
"PAN MACMILLAN"

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,4 +1,249 @@
[
{
"belongs_to_collection": null,
"genres": [
{
"id": 99,
"name": "Documentary"
}
],
"imdb_id": "tt17658964",
"origin_country": [
"US"
],
"overview": "In an unremarkable office space, a select group of aging engineers find themselves at the leading edge of discovery. Fighting outdated technology and time, Voyager\u2019s flight-team pursues humankind\u2019s greatest exploration.",
"poster_path": "/bjIFNp0D4uSKUY2BXRxq8rpsba7.jpg",
"production_countries": [],
"release_date": "2022-03-13",
"spoken_languages": [],
"title": "It\u2019s Quieter in the Twilight",
"tmdb_id": 934298,
"date_added": "2024-04-23"
},
{
"belongs_to_collection": null,
"genres": [
{
"id": 18,
"name": "Drama"
},
{
"id": 10749,
"name": "Romance"
}
],
"imdb_id": "tt0039416",
"overview": "A magazine writer poses as a Jew to expose anti-Semitism.",
"poster_path": "/aCCRbcmigPk00f34hpJuWZuvABo.jpg",
"production_countries": [
{
"iso_3166_1": "US",
"name": "United States of America"
}
],
"release_date": "1947-11-11",
"spoken_languages": [
{
"english_name": "English",
"iso_639_1": "en",
"name": "English"
}
],
"title": "Gentleman's Agreement",
"tmdb_id": 33667,
"date_added": "2024-04-11"
},
{
"belongs_to_collection": null,
"genres": [
{
"id": 36,
"name": "History"
},
{
"id": 10752,
"name": "War"
},
{
"id": 18,
"name": "Drama"
}
],
"imdb_id": "tt0045296",
"overview": "The story of Mexican revolutionary Emiliano Zapata, who led a rebellion against the corrupt, oppressive dictatorship of president Porfirio D\u00edaz in the early 20th century.",
"poster_path": "/vfarxn9ddiaZpRDml8FGhB46Qrc.jpg",
"production_countries": [
{
"iso_3166_1": "US",
"name": "United States of America"
}
],
"release_date": "1952-02-07",
"spoken_languages": [
{
"english_name": "German",
"iso_639_1": "de",
"name": "Deutsch"
},
{
"english_name": "English",
"iso_639_1": "en",
"name": "English"
},
{
"english_name": "Italian",
"iso_639_1": "it",
"name": "Italiano"
}
],
"title": "Viva Zapata!",
"tmdb_id": 1810,
"date_added": "2024-04-11"
},
{
"belongs_to_collection": null,
"genres": [
{
"id": 14,
"name": "Fantasy"
}
],
"imdb_id": "tt9662722",
"overview": "When Pinky discovers that he has the power to read the energy of others, he starts to see people for who they truly are. Which makes life, and losing his virginity, a lot more interesting.",
"poster_path": "/kMUsMBopO5L6m9xj0ZONO9fIzQz.jpg",
"production_countries": [
{
"iso_3166_1": "GB",
"name": "United Kingdom"
}
],
"release_date": "2022-04-01",
"spoken_languages": [
{
"english_name": "English",
"iso_639_1": "en",
"name": "English"
}
],
"title": "Pinky",
"tmdb_id": 696357,
"date_added": "2024-04-11"
},
{
"belongs_to_collection": null,
"genres": [
{
"id": 18,
"name": "Drama"
}
],
"imdb_id": "tt0031828",
"overview": "In a Welsh coal mining valley, a young man with a beautiful singing voice is called upon to make the ultimate sacrifice when a pit disaster threatens.",
"poster_path": "/l1aABDgzG5Ew5X05dGI3th49t0d.jpg",
"production_countries": [
{
"iso_3166_1": "GB",
"name": "United Kingdom"
}
],
"release_date": "1940-04-06",
"spoken_languages": [
{
"english_name": "English",
"iso_639_1": "en",
"name": "English"
}
],
"title": "The Proud Valley",
"tmdb_id": 87060,
"date_added": "2024-04-09"
},
{
"belongs_to_collection": null,
"genres": [
{
"id": 27,
"name": "Horror"
}
],
"imdb_id": "tt9213244",
"overview": "Rumored to have been lost, Antrum appears as a cursed film from the 1970s. Viewers are warned to proceed with caution. It\u2019s said to be a story about a young boy and girl who enter the forest in an attempt to save the soul of their recently deceased pet. They journey to \u201cThe Antrum,\u201d the very spot the devil landed after being cast out of heaven. There, the children begin to dig a hole to hell.",
"poster_path": "/20de4Rcmy4B0L5zyOc3Nc5F7c0D.jpg",
"production_countries": [
{
"iso_3166_1": "CA",
"name": "Canada"
}
],
"release_date": "2018-10-14",
"spoken_languages": [
{
"english_name": "English",
"iso_639_1": "en",
"name": "English"
},
{
"english_name": "Japanese",
"iso_639_1": "ja",
"name": "\u65e5\u672c\u8a9e"
},
{
"english_name": "Hungarian",
"iso_639_1": "hu",
"name": "Magyar"
}
],
"title": "Antrum",
"tmdb_id": 412546,
"date_added": "2024-04-09"
},
{
"belongs_to_collection": null,
"genres": [
{
"id": 35,
"name": "Comedy"
},
{
"id": 18,
"name": "Drama"
},
{
"id": 878,
"name": "Science Fiction"
}
],
"imdb_id": "tt0096863",
"original_language": "hu",
"original_title": "Az \u00e9n XX. sz\u00e1zadom",
"overview": "A tale of twin girls, D\u00f3ra and Lili, who are born in 1880 Budapest at the same moment Thomas Edison presents his electric lightbulb to the world. The sisters are soon orphaned and separated in childhood, and follow different paths: one grows up to be a na\u00efvely idealistic, bomb-toting anarchist, the other a pampered, hedonistic courtesan. Their paths cross once again on the Orient Express on New Year s Eve 1899...",
"poster_path": "/sXdS2zI9cRnZzwyxbjduvxPoj64.jpg",
"production_countries": [
{
"iso_3166_1": "CU",
"name": "Cuba"
},
{
"iso_3166_1": "DE",
"name": "Germany"
},
{
"iso_3166_1": "HU",
"name": "Hungary"
}
],
"release_date": "1989-09-01",
"spoken_languages": [
{
"english_name": "Hungarian",
"iso_639_1": "hu",
"name": "Magyar"
}
],
"title": "My Twentieth Century",
"tmdb_id": 49978,
"date_added": "2024-04-03"
},
{
"belongs_to_collection": null,
"genres": [

View file

@ -1,13 +1,4 @@
[
{
"Title": "Death Stranding",
"Date Started": "2024-01-25",
"Platforms": "PC",
"Developers": "Kojima Productions",
"Date Released": "2019-11-08",
"GiantBomb ID": "54232",
"date_added": "2019-12-04T21:27:08Z"
},
{
"Title": "Ancestors: The Humankind Odyssey",
"Platform": "PC",

View file

@ -1,10 +1,45 @@
[
{
"Title": "Random Notes",
"Developers": "Evan Katz & Josh Roberts",
"Date Released": "2021",
"Date Started": "2024-04-26",
"Date Finished": "2024-04-26",
"BBG ID": "349812"
},
{
"Title": "Pandemic",
"Developers": "Matt Leacock",
"Date Released": "2008",
"Date Started": "2024-04-26",
"Date Finished": "2024-04-26",
"BBG ID": "30549"
},
{
"Title": "Trial by Trolley",
"Developers": "Scott Houser",
"Date Released": "2020",
"Date Started": "2024-04-26",
"Date Finished": "2024-04-26",
"BBG ID": "282171"
},
{
"Title": "Death Stranding",
"Date Started": "2024-01-25",
"Platforms": "PC",
"Developers": "Kojima Productions",
"Date Released": "2019-11-08",
"GiantBomb ID": "54232",
"date_added": "2019-12-04T21:27:08Z",
"date_finished": "2024-04-13",
"Time Played": "62 hours"
},
{
"Title": "Bohnanza: 25th Anniversary Edition",
"Developers": "Uwe Rosenberg",
"Date Released": "2022",
"Date Started": "2024-01-30",
"Date Finisehd": "2024-01-30",
"Date Finished": "2024-01-30",
"BBG ID": "351605"
},
{

View file

@ -1,4 +1,17 @@
[
{
"Title": "the static speaks my name",
"Developers": "the whale husband",
"Date Released": "2015",
"date_added": "2024-04-23"
},
{
"Title": "Botany Manor",
"Developers": "Balloon Studios",
"Date Released": "2023",
"GiantBomb ID": "87888",
"date_added": "2024-04-13T09:23:00Z"
},
{
"Title": "Halo 2600",
"Developers": "",

BIN
data/media.db Normal file

Binary file not shown.

View file

@ -1,4 +1,186 @@
[
{
"overview": "In the wake of a tragic death, Blackthorne finally considers the true nature of Toranaga's plan.",
"name": "A Dream of a Dream",
"air_date": "2024-04-23",
"episode_number": 10,
"season_number": 1,
"tmdb_id": 5146999,
"series": {
"tmdb_id": 126308
},
"date_added": "2024-04-23",
"date_finished": "2024-04-23",
"added_by_id": "10141087"
},
{
"overview": "Mariko arrives in Osaka for the fight of her life. Blackthorne and Yabushige scramble to save their own heads as their options dwindle.",
"name": "Crimson Sky",
"air_date": "2024-04-16",
"episode_number": 9,
"season_number": 1,
"tmdb_id": 5146998,
"series": {
"tmdb_id": 126308
},
"date_added": "2024-04-23",
"date_finished": "2024-04-23",
"added_by_id": "10141086"
},
{
"overview": "Toranaga's defeated clan moves to Edo and awaits their fate. Blackthorne must decide who he fights for - the lord who has turned his back on him, or the ambition that brought him to Japan in the first place.",
"name": "Abyss of Life",
"air_date": "2024-04-09",
"episode_number": 8,
"season_number": 1,
"tmdb_id": 5146997,
"series": {
"tmdb_id": 126308
},
"date_added": "2024-04-22",
"date_finished": "2024-04-22",
"added_by_id": "10141085"
},
{
"overview": "Outplayed by new alliances in Osaka, Toranaga is forced to carve out a new deal with a long lost family member.",
"name": "A Stick of Time",
"air_date": "2024-04-02",
"episode_number": 7,
"season_number": 1,
"tmdb_id": 5146996,
"series": {
"tmdb_id": 126308
},
"date_added": "2024-04-22",
"date_finished": "2024-04-22",
"added_by_id": "10141084"
},
{
"overview": "Lady Ochiba returns to Osaka in order to accelerate the Regents' campaign against Toranaga. In Ajiro, Toranaga tests Mariko's loyalty to his cause.",
"name": "Ladies of the Willow World",
"air_date": "2024-03-26",
"episode_number": 6,
"season_number": 1,
"tmdb_id": 4924958,
"series": {
"tmdb_id": 126308
},
"date_added": "2024-04-21",
"date_finished": "2024-04-21",
"added_by_id": "10141083"
},
{
"overview": "Blackthorne and Mariko struggle to contain the secret that could get them both killed. Yabushige searches for the spy who has betrayed his intentions to Lord Toranaga.",
"name": "Broken to the Fist",
"air_date": "2024-03-19",
"episode_number": 5,
"season_number": 1,
"tmdb_id": 4924957,
"series": {
"tmdb_id": 126308
},
"date_added": "2024-04-21",
"date_finished": "2024-04-21",
"added_by_id": "10141082"
},
{
"overview": "Blackthorne and Mariko test their new alliance as they train Toranaga's gun regiment for war. Yabushige must navigate his past promises to Ishido when an old friend comes to the village.",
"name": "The Eightfold Fence",
"air_date": "2024-03-12",
"episode_number": 4,
"season_number": 1,
"tmdb_id": 4924951,
"series": {
"tmdb_id": 126308
},
"date_added": "2024-04-21",
"date_finished": "2024-04-21",
"added_by_id": "10141081"
},
{
"overview": "After Blackthorne survives a brazen assassination attempt, Toranaga realizes he must ferry his allies out of Osaka or risk certain defeat.",
"name": "Tomorrow Is Tomorrow",
"air_date": "2024-03-05",
"episode_number": 3,
"season_number": 1,
"tmdb_id": 4924949,
"series": {
"tmdb_id": 126308
},
"date_added": "2024-04-16",
"date_finished": "2024-04-16",
"added_by_id": "10141080"
},
{
"overview": "Blackthorne's arrival in Osaka stirs up a hornet's nest of rivalries. Mariko is trapped between her cause and her faith when she must translate for the barbarian in Lord Toranaga's custody.",
"name": "Servants of Two Masters",
"air_date": "2024-02-27",
"episode_number": 2,
"season_number": 1,
"tmdb_id": 3251731,
"series": {
"tmdb_id": 126308
},
"date_added": "2024-04-16",
"date_finished": "2024-04-16",
"added_by_id": "10141079"
},
{
"overview": "Destinies converge in Japan after a barbarian ship washes ashore in a poor fishing village. Meanwhile, in Osaka, Lord Toranaga finds himself outplayed by his enemies.",
"name": "Anjin",
"air_date": "2024-02-27",
"episode_number": 1,
"season_number": 1,
"tmdb_id": 2975040,
"series": {
"tmdb_id": 126308
},
"date_added": "2024-04-16",
"date_finished": "2024-04-16",
"added_by_id": "9240063"
},
{
"overview": "When Ilpalazzo orders Excel and Hyatt to learn how to control the masses through their most popular sports, the duo end up working in the rather less than mainstream field of ten pin bowling! However, what looks to be nothing more than a quiet part time job in a bowling alley soon becomes far more dangerous when the alley is taken over by an evil terrorist group of middle-aged bowling fanatics! Now only woman stands in the way of these miscreants\u2014the over-energetic yet under-competent Excel Excel\u2026",
"name": "Bowling Girls",
"air_date": "1999-12-02",
"episode_number": 9,
"season_number": 1,
"tmdb_id": 408100,
"series": {
"tmdb_id": 9519
},
"date_added": "2024-04-15",
"date_finished": "2024-04-15",
"added_by_id": "86589"
},
{
"overview": "It\u2019s an all-girl fan service fest as Excel and Hyatt take off to the swimming pool, accompanied by local girl Cosette. But even as they laze in the sun and enjoy all the clich\u00e9s of a summer episode, it appears that Cosette may have a deadlier mission in mind than simply taking a quick dip in the pool. Could such a cute little girl really be a sinister assassin? On a show like this, anything seems likely\u2026",
"name": "Increase Ratings Week",
"air_date": "1999-11-25",
"episode_number": 8,
"season_number": 1,
"tmdb_id": 408099,
"series": {
"tmdb_id": 9519
},
"date_added": "2024-04-13",
"date_finished": "2024-04-13",
"added_by_id": "86588"
},
{
"overview": "When Kabapu sends Watanabe and his colleagues to investigate the sewers surrounding the Across base, Ilpalazzo dispatches Excel and Hyatt to deal with the intruders. However, even as the duo familiarise themselves with the Across security system, they discover a far more sinister breed of intruders lurking in the sewers- an army of Puchuus led by the nefarious Puchuu Queen! Will Excel and the others be able to stand against this overwhelming cute force of evil?",
"name": "Melody of the Underground Passage",
"air_date": "1999-11-18",
"episode_number": 7,
"season_number": 1,
"tmdb_id": 408098,
"series": {
"tmdb_id": 9519
},
"date_added": "2024-04-13",
"date_finished": "2024-04-13",
"added_by_id": "86587"
},
{
"name": "The Saphrax Protocol",
"overview": "Deep inside Guild Headquarters, the Monarch and 21 face their ultimate ordeal; the Venture brothers confront one of their own.",

View file

@ -1,15 +1,149 @@
[
{
"tmdb_id": 2418,
"created_by": [
{
"id": 2001427,
"credit_id": "5aaeaeb8925141762d0131ca",
"name": "Koshi Rikudo",
"original_name": "Koshi Rikudo",
"gender": 2,
"profile_path": null
}
],
"episode_run_time": [
25
],
"first_air_date": "1999-10-07",
"genres": [
{
"id": 16,
"name": "Animation"
},
{
"id": 35,
"name": "Comedy"
},
{
"id": 10759,
"name": "Action & Adventure"
},
{
"id": 10765,
"name": "Sci-Fi & Fantasy"
}
],
"in_production": false,
"languages": [
"ja"
],
"last_air_date": "2000-03-30",
"last_episode_to_air": {
"id": 408076,
"overview": "In this aptly-named epilogue, an aborted attempt at making a musical version of Excel Saga is replaced by an exercise in pushing the boundaries of good taste for even Japanese television. As a small sampling of the insanity, Excel and Hyatt (the former more deranged than usual, the latter more sickly than usual) switch bodies, Cosette returns only to get naughty with Dr. Shioji, Watanabe takes Hyatt (in Excel\u2019s body) to a love hotel, Nabeshin gets married, and there\u2019s way too much blood and pointless violence. What better way to end a \"Quack Experimental Anime\"?",
"name": "Going Too Far",
"vote_average": 0.0,
"vote_count": 0,
"air_date": "2000-03-30",
"episode_number": 26,
"episode_type": "finale",
"production_code": "",
"runtime": 25,
"season_number": 1,
"show_id": 9519,
"still_path": "/jNYsVN4R78jeJVzATG4NocUSn4H.jpg"
},
"name": "Excel Saga",
"next_episode_to_air": null,
"networks": [
{
"id": 98,
"logo_path": "/jnuO8pZNEBLEq5YaOP1f5OkmG91.png",
"name": "TV Tokyo",
"origin_country": "JP"
},
{
"id": 171,
"logo_path": "/5S9xrNouNbaYlhDkaZopIpcmbp2.png",
"name": "ANIMAX",
"origin_country": "JP"
}
],
"number_of_episodes": 26,
"number_of_seasons": 1,
"origin_country": [
"JP",
"US"
],
"original_language": "ja",
"original_name": "\u3078\u3063\u307d\u3053\u5b9f\u9a13\u30a2\u30cb\u30e1\u30fc\u30b7\u30e7\u30f3\u3000\u30a8\u30af\u30bb\u30eb\u2665\u30b5\u30fc\u30ac",
"overview": "Hyperactive Excel does anything and everything to try to please her lord, Il Palazzo, who wants to take over the planet. Excel\u2019s misadventures takes her and her partner, the ever-dying Hyatt, all over the world, meeting several strange people as they go. Everything is bizarre and goofy, as any kind of anime or entertainment genre gets mocked and spoofed.",
"poster_path": "/55mHvsKigtxCx1Ew8iM5snsmlp2.jpg",
"production_countries": [
{
"iso_3166_1": "JP",
"name": "Japan"
},
{
"iso_3166_1": "US",
"name": "United States of America"
}
],
"seasons": [
{
"air_date": "1999-10-07",
"episode_count": 26,
"id": 19376,
"name": "Season 1",
"overview": "",
"poster_path": "/twJC120FmiPcAxcVQQkceSRF23W.jpg",
"season_number": 1,
"vote_average": 6.0
}
],
"spoken_languages": [
{
"english_name": "Japanese",
"iso_639_1": "ja",
"name": "\u65e5\u672c\u8a9e"
}
],
"tmdb_id": 9519,
"date_added": "2024-04-23",
"date_started": "2024-03-03",
"added_by_id": "9519"
},
{
"first_air_date": "2024-02-27",
"genres": [
{
"id": 18,
"name": "Drama"
},
{
"id": 10768,
"name": "War & Politics"
}
],
"languages": [
"en",
"ja"
],
"name": "Sh\u014dgun",
"origin_country": [
"US"
],
"overview": "Hank and Dean Venture, with their father Doctor Venture and faithful bodyguard Brock Samson, go on wild adventures facing megalomaniacs, zombies, and suspicious ninjas, all for the glory of adventure. Or something like that.",
"poster_path": "/ckQE1aLYQkRpp2HmHljiELAiOr1.jpg",
"first_air_date": "2004-08-07",
"name": "The Venture Bros.",
"date_added": "2024-01-17",
"date_started": "2024-02-11"
"overview": "In Japan in the year 1600, at the dawn of a century-defining civil war, Lord Yoshii Toranaga is fighting for his life as his enemies on the Council of Regents unite against him, when a mysterious European ship is found marooned in a nearby fishing village.",
"poster_path": "/7O4iVfOMQmdCSxhOg1WnzG1AgYT.jpg",
"production_countries": [
{
"iso_3166_1": "US",
"name": "United States of America"
}
],
"tmdb_id": 126308,
"date_added": "2024-04-16",
"date_started": "2024-04-16",
"added_by_id": "126308"
},
{
"tmdb_id": 2710,

File diff suppressed because it is too large Load diff

View file

@ -867,9 +867,8 @@
"date_added": "2024-01-14"
},
{
"Show Title": "Baker Boys: Inside the Surge",
"TVDB ID": "252424",
"IMDB ID": ""
"name": "Baker Boys: Inside the Surge",
"tvdb_id": "252424"
},
{
"tmdb_id": 9085,
@ -2156,8 +2155,8 @@
"date_added": "2024-01-17"
},
{
"Show Title": "Ecstasy of Car",
"IMDB ID": ""
"name": "Ecstasy of Car",
"tmdb_id": 251362
},
{
"tmdb_id": 21750,
@ -2658,8 +2657,8 @@
"date_added": "2024-01-17"
},
{
"Show Title": "Generation Cryo",
"IMDB ID": ""
"name": "Generation Cryo",
"tvdb_id": 274811
},
{
"tmdb_id": 4455,
@ -3129,8 +3128,8 @@
"date_added": "2024-01-17"
},
{
"Show Title": "HydeWars",
"IMDB ID": ""
"name": "HydeWars",
"tvdb_id": 332856
},
{
"tmdb_id": 2730,
@ -3534,8 +3533,8 @@
"date_added": "2024-01-17"
},
{
"Show Title": "KSTV",
"IMDB ID": ""
"name": "KSTV",
"tvdb_id": 386125
},
{
"tmdb_id": 4250,
@ -3770,10 +3769,6 @@
"name": "Long Way Round",
"date_added": "2024-01-17"
},
{
"Show Title": "Looney Tunes",
"IMDB ID": ""
},
{
"tmdb_id": 87107,
"origin_country": [
@ -4192,8 +4187,8 @@
"date_added": "2024-01-17"
},
{
"Show Title": "Monarchy with David Starkey",
"IMDB ID": ""
"name": "Monarchy with David Starkey",
"tmdb_id": 9484
},
{
"tmdb_id": 30981,
@ -5664,8 +5659,8 @@
"date_added": "2024-01-17"
},
{
"Show Title": "Star Trek: New Voyages",
"IMDB ID": ""
"name": "Star Trek: New Voyages",
"tvdb_id": "79256"
},
{
"tmdb_id": 1992,
@ -5859,8 +5854,8 @@
"date_added": "2024-01-17"
},
{
"Show Title": "SuperF***ers",
"IMDB ID": ""
"name": "SuperF***ers",
"tvdb_id": "265660"
},
{
"tmdb_id": 3108,
@ -5942,8 +5937,8 @@
"date_added": "2024-01-17"
},
{
"Show Title": "Tenacious D in Post-Apocalypto",
"IMDB ID": ""
"name": "Tenacious D in Post-Apocalypto",
"tvdb_id": "353917"
},
{
"tmdb_id": 21729,
@ -7390,8 +7385,8 @@
"date_added": "2024-01-17"
},
{
"Show Title": "Theater of the Aire",
"IMDB ID": ""
"name": "Theater of the Aire",
"tvdb_id": "369510"
},
{
"tmdb_id": 907,
@ -7988,7 +7983,7 @@
"date_added": "2024-01-17"
},
{
"Show Title": "Yu-Gi-Oh: The Abridged Series",
"IMDB ID": ""
"name": "Yu-Gi-Oh: The Abridged Series",
"tvdb_id": "253797"
}
]

View file

@ -13,6 +13,8 @@ pathspec==0.12.1
platformdirs==4.1.0
pylint==3.0.3
python-dotenv==1.0.0
python-slugify==8.0.4
requests==2.31.0
text-unidecode==1.3
tomlkit==0.12.3
urllib3==2.2.0

View file

@ -275,6 +275,8 @@ def import_by_id(import_id, media_type, log) -> dict | None:
"".join(re.findall(r"\d+", import_id)), media_type
)
logger.error("Invalid media_type!")
def import_from_tmdb_by_external_id(external_id, media_type) -> dict:
"""Retrieve a film, TV show or TV episode from TMDB using an IMDB or TVDB ID"""

277
scripts/json_to_sql.py Normal file
View file

@ -0,0 +1,277 @@
import sqlite3
import json
import traceback
json_keys = {
"books": {
"name_key": "title",
"item": {
"title",
"subtitle",
"edition_name",
"full_title",
"ol_id",
"isbn_10",
"isbn_13",
"added_by_id",
"covers",
"publish_date",
"publishers",
"physical_format",
"description",
},
"genres": "genres",
"collections": "series",
"work": "work",
"creators": "authors",
"languages": "languages",
"countries": "publish_country",
"entry": {"date_added", "date_started", "date_finished", "comments"},
},
"films": {
"name_key": "title",
"original_name_key": "original_title",
"original_language_key": "original_language",
"item": {
"title",
"imdb_id",
"tmdb_id",
"added_by_id",
"poster_path",
"release_date",
"overview",
"original_title",
"original_language",
},
"collections": {"key": "belongs_to_collection", "fields": {"name"}},
"languages": {
"key": "spoken_languages",
"fields": {"english_name", "iso_639_1"},
},
"countries": {"key": "production_countries", "fields": {"name", "iso_3166_1"}},
"entry": {"date_added", "date_started", "date_finished", "comments"},
},
"tv-series": {
"name_key": "name",
"original_name_key": "original_name",
"original_language_key": "original_language",
"item": {
"name",
"tmdb_id",
"tvdb_id",
"added_by_id",
"poster_url",
"overview",
"first_air_date",
"original_name",
"original_language",
},
"languages": {
"key": "spoken_languages",
"fields": {"english_name", "iso_639_1"},
},
"countries": {"key": "production_countries", "fields": {"name", "iso_3166_1"}},
"entry": {"date_added", "date_started", "date_finished", "comments"},
},
"tv-episodes": {
"name_key": "name",
"original_name_key": "original_name",
"original_language_key": "original_language",
"item": {
"name",
"tmdb_id",
"tvdb_id",
"added_by_id",
"overview",
"air_date",
"series",
"episode_number",
"season_number",
},
"languages": {
"key": "spoken_languages",
"fields": {"english_name", "iso_639_1"},
},
"countries": {"key": "production_countries", "fields": {"name", "iso_3166_1"}},
"entry": {"date_added", "date_finished", "comments"},
},
}
sql_columns = {
"films": {
"unique_keys": ["tmdb_id"],
"languages": {
"keys": ["name", "iso_639_code"],
"unique_keys": ["iso_639_code"],
"join_keys": ["film_id", "language_id"],
},
"countries": {
"keys": ["name", "iso_3166_code"],
"unique_keys": ["iso_3166_code"],
"join_keys": ["film_id", "country_id"],
}
},
"tv-episodes": {
"unique_keys": ["tmdb_id", "imdb_id"],
"languages": {
"keys": ["name", "iso_639_code"],
"unique_keys": ["iso_639_code"],
"join_keys": ["tv_episode_id", "language_id"],
}
},
"tv-series": {
"unique_keys": ["tmdb_id", "imdb_id"],
"languages": {
"keys": ["name", "iso_639_code"],
"unique_keys": ["iso_639_code"],
"join_keys": ["tv_episode_id", "language_id"],
},
"countries": {
"keys": ["name", "iso_3166_code"],
"unique_keys": ["iso_3166_code"],
"join_keys": ["tv_series_id", "country_id"],
}
}
}
def insert_from_json(media_type, log):
media_type_pl = get_media_type_pl(media_type)
json_path = f"./data/{media_type_pl}/{log}.json"
db_path = "./data/media.db"
with open(json_path, "r") as file:
data = json.load(file)
print(f"Results: {len(data)}")
conn = sqlite3.connect(db_path)
conn.isolation_level = None
cur = conn.cursor()
cur.execute('BEGIN')
try:
for entry in reversed(data):
print(f"Importing {entry.get( json_keys[media_type_pl]['name_key'] )}")
# Insert item
item_entry = {
key: entry[key] for key in entry.keys() & json_keys[media_type_pl]["item"]
}
if item_entry.get(json_keys[media_type_pl]['original_name_key']) is not None:
item_entry["title"] = item_entry.pop( json_keys[media_type_pl]['name_key'] )
item_entry["title_original"] = item_entry.pop(json_keys[media_type_pl]['original_name_key'] )
item_entry["title_original_language"] = item_entry.pop(json_keys[media_type_pl]['original_language_key'] )
else:
item_entry["title"] = item_entry.pop( json_keys[media_type_pl]['name_key'] )
keys = ", ".join(item_entry.keys())
unique_keys = ", ".join(sql_columns[media_type_pl]['unique_keys'])
question_marks = ", ".join(["?" for _ in item_entry])
values = tuple(item_entry.values())
cur.execute(
f"INSERT INTO '{media_type_pl}' ({keys}) VALUES ({question_marks}) ON CONFLICT({unique_keys}) DO UPDATE SET ({keys}) = ({question_marks}) RETURNING id",
values + values,
)
row = cur.fetchone()
(inserted_id,) = row if row else None
# Join tables
for join_type in ["languages", "countries"]:
if entry.get( json_keys[media_type_pl][join_type]["key"] ) is not None:
for join_item in entry.get( json_keys[media_type_pl][join_type]["key"] ):
print(f"Importing {join_type} {join_item}")
values = {
key: join_item[key]
for key in join_item.keys()
& json_keys[media_type_pl][join_type]["fields"]
}
insert_join(
inserted_id,
f"{join_type}",
f"{media_type_pl}_{join_type}",
sql_columns[media_type_pl][join_type]["join_keys"],
sql_columns[media_type_pl][join_type]["keys"],
values,
sql_columns[media_type_pl][join_type]["unique_keys"],
cur,
conn
)
# Log Entry
item_log_entry = {
key: entry[key] for key in entry.keys() & json_keys[media_type_pl]["entry"]
}
if item_log_entry.get("date_added") is not None:
item_log_entry["log"] = log
item_log_entry[f"{media_type}_id"] = inserted_id
print(f"Importing log entry added {item_log_entry.get('date_added')}")
keys = ", ".join(item_log_entry.keys())
question_marks = ", ".join(["?" for _ in item_log_entry])
values = tuple(item_log_entry.values())
cur.execute(
f"INSERT INTO '{media_type_pl}_log-entries' ({keys}) VALUES ({question_marks})",
values,
)
else:
print(f"No log details for {entry.get('name')}!")
except Exception:
print(traceback.format_exc())
cur.execute('ROLLBACK')
else:
conn.commit()
conn.close()
def insert_join(
media_id,
table_name,
join_table_name,
join_keys,
data_keys,
data_values,
data_unique,
cur,
conn,
):
keys = ", ".join(data_keys)
unique_keys = ", ".join(data_unique)
question_marks = ", ".join(["?" for _ in data_keys])
values = tuple(data_values)
cur.execute(
f"INSERT INTO '{table_name}' ({keys}) VALUES ({question_marks}) ON CONFLICT({unique_keys}) DO UPDATE SET ({keys}) = ({question_marks}) RETURNING id",
values + values,
)
row = cur.fetchone()
(data_id,) = row if row else None
if data_id is not None:
keys = ", ".join(join_keys)
print(f"Matching item ID {media_id} to data ID {data_id}")
cur.execute(
f"INSERT INTO '{join_table_name}' ({keys}) VALUES ({media_id}, {data_id}) ON CONFLICT({keys}) DO NOTHING"
)
def get_media_type_pl(media_type):
if media_type in [ 'tv-series' ]:
return media_type
else:
return media_type + 's'
# insert_from_json('./data/tv-series/log.json', './data/media.db', 'tv-series', 'log')
#insert_from_json("./data/tv-series/wishlist.json", "./data/media.db", "tv-series", "wishlist")
#insert_from_json("./data/tv-series/current.json", "./data/media.db", "tv-series", "current")
insert_from_json('film', 'log')
#insert_from_json("./data/films/wishlist.json", "./data/media.db", "films", "wishlist")

View file

@ -7,6 +7,7 @@ import os
import re
import time
import requests
from slugify import slugify
from dotenv import load_dotenv
from add_item import cleanup_result, import_by_id, setup_logger
@ -28,7 +29,7 @@ def process_log(media_type, log) -> None:
logger.info(f"Processing {media_type}/{log}")
with open(f"./data/{media_type}/{log}.json", "r", encoding='utf-8') as log_file:
with open(f"./data/{media_type}/{log}.json", "r", encoding="utf-8") as log_file:
log_items = json.load(log_file)
log_item_values = {}
@ -42,171 +43,187 @@ def process_log(media_type, log) -> None:
id_key = "gb_id"
for i, item in enumerate(log_items):
try:
if id_key not in item and "skip" not in item:
if media_type in ["films", "books"]:
item_title = item["Title"]
elif "tv-episodes" == media_type:
item_title = item["Episode Title"]
elif "tv-series" == media_type:
item_title = item["Show Title"]
if id_key not in item:# and "skip" not in item:
if media_type in ["films", "books"]:
item_title = item["Title"]
elif "tv-episodes" == media_type:
item_title = item["Episode Title"]
elif "tv-series" == media_type:
item_title = item["Show Title"]
logger.info(f"Processing {item_title}")
logger.info(f"Processing {item_title} ({item['Author']})")
# Rename pre-existing fields
if "Date Added" in item:
log_item_values["date_added"] = item["Date Added"]
del item["Date Added"]
# Rename pre-existing fields
if "Date Added" in item:
log_item_values["date_added"] = item["Date Added"]
del item["Date Added"]
if "date_added" in item:
log_item_values["date_added"] = item["date_added"]
if "date_added" in item:
log_item_values["date_added"] = item["date_added"]
if "Date Started" in item:
log_item_values["date_started"] = item["Date Started"]
del item["Date Started"]
if "Date Started" in item:
log_item_values["date_started"] = item["Date Started"]
del item["Date Started"]
if "date_started" in item:
log_item_values["date_started"] = item["date_started"]
if "date_started" in item:
log_item_values["date_started"] = item["date_started"]
if "Date Finished" in item:
log_item_values["date_finished"] = item["Date Finished"]
del item["Date Finished"]
if "Date Read" in item:
if item["Date Finished"] == item["Date Read"]:
del item["Date Read"]
else:
raise Exception(f"'Date Read' != 'Date Finished' for {item['Title']}")
if "date_finished" in item:
log_item_values["date_finished"] = item["date_finished"]
if "Read Count" in item:
log_item_values["read_count"] = item["Read Count"]
del item["Read Count"]
if "read_count" in item:
log_item_values["read_count"] = item["read_count"]
if "Date Watched" in item:
log_item_values["date_finished"] = item["Date Watched"]
del item["Date Watched"]
if "Rewatch" in item:
log_item_values["is_repeat"] = item["Rewatch"]
del item["Rewatch"]
if "Comments" in item:
log_item_values["comments"] = item["Comments"]
del item["Comments"]
if "Series Title" in item:
log_item_values["series_title"] = item["Series Title"]
del item["Series Title"]
if "Episode Title" in item:
log_item_values["name"] = item["Episode Title"]
del item["Episode Title"]
if "Episode Number" in item:
if re.search("[0-9]+x[0-9]+", item["Episode Number"]) is not None:
season_no, _, episode_no = log_item_values[
"episode_number"
].split("x")
elif (
re.search("S[0-9]+E[0-9]+", item["Episode Number"]) is not None
):
season_no, _, episode_no = log_item_values[
"episode_number"
].split("E")
elif re.search("E[0-9]+", item["Episode Number"]) is not None:
season_no = None
episode_no = item["episode_number"][1:]
if "Date Finished" in item:
log_item_values["date_finished"] = item["Date Finished"]
del item["Date Finished"]
if "Date Read" in item:
if item["Date Finished"] == item["Date Read"]:
del item["Date Read"]
else:
logger.error(
f"Invalid episode number format '{item['Episode Number']}'"
raise Exception(
f"'Date Read' != 'Date Finished' for {item['Title']}"
)
return
log_item_values["season_number"] = season_no
log_item_values["episode_number"] = episode_no
del item["Episode Number"]
if "date_finished" in item:
log_item_values["date_finished"] = item["date_finished"]
if "IMDB ID" in item and item["IMDB ID"] != "":
new_log_item = import_by_id(item["IMDB ID"], media_type)
if "Read Count" in item:
log_item_values["read_count"] = item["Read Count"]
del item["Read Count"]
elif "books" == media_type and "wishlist" == log:
ol_work_id = re.search("OL[0-9]+W", input(f"Enter OpenLibrary Work ID for '{item_title}' ({item['Author']}): "))
try:
new_log_item = import_by_id(ol_work_id[0], media_type, log)
except:
new_log_item = item
item["skip"] = True
logger.info("Skipping…")
if "read_count" in item:
log_item_values["read_count"] = item["read_count"]
elif "ISBN13" in item and item["ISBN13"] != "" and item["ISBN13"] is not None:
new_log_item = import_by_id(item["ISBN13"], media_type, log)
if "Date Watched" in item:
log_item_values["date_finished"] = item["Date Watched"]
del item["Date Watched"]
elif "ISBN" in item and item["ISBN"] != "" and item["ISBN"] is not None:
new_log_item = import_by_id(item["ISBN13"], media_type, log)
if "Rewatch" in item:
log_item_values["is_repeat"] = item["Rewatch"]
del item["Rewatch"]
if "Comments" in item:
log_item_values["comments"] = item["Comments"]
del item["Comments"]
if "Series Title" in item:
log_item_values["series_title"] = item["Series Title"]
del item["Series Title"]
if "Episode Title" in item:
log_item_values["name"] = item["Episode Title"]
del item["Episode Title"]
if "Episode Number" in item:
if re.search("[0-9]+x[0-9]+", item["Episode Number"]) is not None:
season_no, _, episode_no = log_item_values[
"episode_number"
].split("x")
elif (
re.search("S[0-9]+E[0-9]+", item["Episode Number"]) is not None
):
season_no, _, episode_no = log_item_values[
"episode_number"
].split("E")
elif re.search("E[0-9]+", item["Episode Number"]) is not None:
season_no = None
episode_no = item["episode_number"][1:]
else:
new_log_item = import_by_details(item, item_title, media_type)
logger.error(
f"Invalid episode number format '{item['Episode Number']}'"
)
return
log_item_values["season_number"] = season_no
log_item_values["episode_number"] = episode_no
del item["Episode Number"]
if "IMDB ID" in item and item["IMDB ID"] != "":
new_log_item = import_by_id(item["IMDB ID"], media_type)
elif "books" == media_type and "wishlist" == log:
new_log_item = import_by_details(item, item_title, media_type)
if new_log_item is None:
if media_type in ["films", "tv-series", "tv-episodes"] and "imdb_id" not in item:
item["imdb_id"] = input(f"Enter IMDB ID for {item_title}: ")
ol_work_id = input(
f"Enter OpenLibrary Work ID for '{item_title}' ({item['Author']}), or 'd' to delete the record: "
)
if re.search("tt[0-9]+", item["imdb_id"]) is not None:
log_items[i] = import_by_id(item["imdb_id"], media_type)
if 'd' == ol_work_id:
logger.info("Deleting…")
del log_items[i]
continue
ol_work_id = re.search("OL[0-9]+W", ol_work_id)
try:
new_log_item = import_by_id(ol_work_id[0], media_type, log)
except:
new_log_item = item
new_log_item["skip"] = True
logger.info("Skipping…")
elif (
"ISBN13" in item
and item["ISBN13"] != ""
and item["ISBN13"] is not None
):
new_log_item = import_by_id(item["ISBN13"], media_type, log)
elif "ISBN" in item and item["ISBN"] != "" and item["ISBN"] is not None:
new_log_item = import_by_id(item["ISBN13"], media_type, log)
else:
new_log_item = import_by_details(item, item_title, media_type)
if new_log_item is None:
if (
media_type in ["films", "tv-series", "tv-episodes"]
and "imdb_id" not in item
):
item["imdb_id"] = input(f"Enter IMDB ID for {item_title}: ")
if re.search("tt[0-9]+", item["imdb_id"]) is not None:
log_items[i] = import_by_id(item["imdb_id"], media_type)
with open(
f"./data/{media_type}/{log}.json", "w", encoding="utf-8"
) as log_file:
json.dump(log_items, log_file, indent=4)
elif "books" == media_type:
if "ISBN" not in item and "ISBN13" not in item:
item["ISBN"] = input(f"Enter ISBN for {item_title}: ")
if re.search("[0-9-]+", item["ISBN"]) is not None:
log_items[i] = import_by_id(item["ISBN"], media_type)
with open(
f"./data/{media_type}/{log}.json",
"w",
encoding='utf-8'
encoding="utf-8",
) as log_file:
json.dump(log_items, log_file, indent=4)
elif "books" == media_type:
if "ISBN" not in item and "ISBN13" not in item:
item["ISBN"] = input(f"Enter ISBN for {item_title}: ")
if re.search("[0-9-]+", item["ISBN"]) is not None:
log_items[i] = import_by_id(item["ISBN"], media_type)
with open(
f"./data/{media_type}/{log}.json",
"w",
encoding='utf-8'
) as log_file:
json.dump(log_items, log_file, indent=4)
else:
logger.warning(f"Skipped '{item_title}'")
log_items[i]["skip"] = True
else:
logger.warning(f"Skipped {item_title}")
logger.warning(f"Skipped '{item_title}'")
log_items[i]["skip"] = True
else:
log_items[i] = new_log_item
logger.warning(f"Skipped {item_title}")
if i % 3 == 0:
with open(
f"./data/{media_type}/{log}.json",
"w",
encoding='utf-8'
) as log_file:
json.dump(log_items, log_file, indent=4)
else:
log_items[i] = new_log_item
if log_items[i] is not None:
log_items[i] |= log_item_values
if i % 3 == 0:
with open(
f"./data/{media_type}/{log}.json", "w", encoding="utf-8"
) as log_file:
json.dump(log_items, log_file, indent=4)
logger.info("Saved…")
except KeyError:
print(json.dumps(item, indent=4))
if log_items[i] is not None:
log_items[i] |= log_item_values
with open(f"./data/{media_type}/{log}.json", "w", encoding='utf-8') as log_file:
with open(f"./data/{media_type}/{log}.json", "w", encoding="utf-8") as log_file:
json.dump(log_items, log_file, indent=4)
logger.info(f"Finished processing {media_type}/{log}")
@ -222,12 +239,69 @@ def import_by_details(item, item_title, media_type) -> dict:
return # import_from_tvdb_by_details(item, item_title, media_type)
if media_type in ["books"]:
return # import_from_openlibrary_by_details(item, item_title, media_type)
return import_from_openlibrary_by_details(item, item_title, media_type)
if media_type in ["games"]:
return # import_from_igdb_by_details(item, item_title, media_type)
def import_from_openlibrary_by_details(item, item_title, media_type) -> dict | None:
"""Retrieve a book from OpenLibrary using a title and author name"""
logger.info(f"Importing '{item_title}'")
api_url = f"https://openlibrary.org/search.json?title={slugify(item['Title'].split(':')[0], separator='%20')}&author={slugify(item['Author'], separator='%20')}"
# Sending API request
response = requests.get(api_url, headers={"accept": "application/json"}, timeout=15)
# Process the response
if 200 == response.status_code:
logger.debug(response.status_code)
elif 429 == response.status_code:
time.sleep(2)
return import_from_openlibrary_by_details(item, item_title, media_type)
elif 404 == response.status_code:
logger.error(f"{response.status_code}: Not Found for title '{item_title}'")
return None
else:
raise Exception(f"Error {response.status_code}: {response.text}")
results = json.loads(response.text)
logger.info(f"Found {results['num_found']} result{'s' if results['num_found'] != 1 else ''}")
if 0 < results["num_found"]:
result = results['docs'][0]
if 1 == results["num_found"]:
logger.info(f"Selecting OL ID {result['key']}")
item_id_parsed = re.search("(OL|tt)?[0-9]+[WMA]?", result['key'])
if item_id_parsed is not None:
return import_by_id(item_id_parsed[0], "books", "wishlist")
else:
if result['title'] == item['Title'].split(':')[0] and result['author_name'][0] == item['Author']:
logger.info(f"First result ({result['key']}) is a match!")
item_id_parsed = re.search("(OL|tt)?[0-9]+[WMA]?", result['key'])
if item_id_parsed is not None:
return import_by_id(item_id_parsed[0], "books", "wishlist")
else:
print(json.dumps({k: result.get(k, None) for k in ('author', 'title', 'first_publish_year')}, indent=4))
is_correct = input("Is this the correct result? [y/n]: ")
if "y" == is_correct:
logger.info(f"Selecting OL ID {result['key']}")
item_id_parsed = re.search("(OL|tt)?[0-9]+[WMA]?", result['key'])
if item_id_parsed is not None:
return import_by_id(item_id_parsed[0], "books", "wishlist")
logger.info(f"Returning nothing…")
return None
def import_from_tmdb_by_details(item, item_title, media_type) -> dict:
"""Retrieve a film or TV series from TMDB using its title"""
@ -244,7 +318,7 @@ def import_from_tmdb_by_details(item, item_title, media_type) -> dict:
"year": item["Release Year"] if "Release Year" in item else None,
},
headers={"Authorization": f"Bearer {TMDB_API_KEY}"},
timeout=15
timeout=15,
)
# Process the response
@ -281,17 +355,17 @@ def import_from_tmdb_by_details(item, item_title, media_type) -> dict:
logger.warning(f"Returned more than one {media_type} for '{item_title}':\n")
print(
json.dumps(
filtered_response_data
if len(filtered_response_data) > 0
else response_data,
(
filtered_response_data
if len(filtered_response_data) > 0
else response_data
),
indent=4,
)
)
last_index = len(filtered_response_data if frd_len > 0 else response_data) - 1
idx = input(
f"\nEnter the index of the result to use [0-{last_index}]: "
)
idx = input(f"\nEnter the index of the result to use [0-{last_index}]: ")
if "" != idx:
try: